HowTo: Erste Schritte mit dem MEF (Hello MEF!)

In fast jeder Applikation kann man Plugins hinzufügen. In .NET 4.0 kommt eine neue Möglichkeit hinzu die man heute bereits ausprobieren kann. Die Rede ist von dem "Managed Extensibility Framework" – kurz MEF.
MEF soll auch in Visual Studio 2010 selbst einzughalten (siehe PDC Keynote von Scott Guthrie). Dazu kann ich auch die PDC Session von Scott Hanselman über sein "BabySmash" empfehlen.

Addins? Gab es da nicht schonmal was?
Es gibt seit .NET 3.5 einen "System.AddIn" Namensraum. Meiner Meinung nach war es relativ kompliziert Addins zu entwickeln. Allerdings soll MEF und System.Addin gut zusammenarbeiten.

Was braucht man?
Alles was man braucht findet man auf der Codeplex Seite. Einfach den neusten Release runterladen und die 2 DLLs in eigene Projekte einsetzen (Achtung – es befindet sich noch in Entwicklung und kann sich jederzeit ändern).

Hello World! Hallo Welt! Hello MEF! – Vorbereitung

Projektstruktur:

image

Wir haben einen einfachen Serviceinterface namens "IHelloService":

    public interface IHelloService
    {
        string GetHelloMessage();
    }

In dem HelloMEF.English / German Projekt haben wir folgenden Code (hier für das englische Plugin) :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HelloMEF.App;
using System.ComponentModel.Composition;

namespace HelloMEF.English
{
    [Export(typeof(IHelloService))]
    public class EnglishHelloService : IHelloService
    {
        public string GetHelloMessage()
        {
            return "Hello World!";
        }
    }
}

Wichtig hier ist das "Export" Attribut aus dem "System.ComponentModel.Composite" (MEF) Namespace.

Damit wird ausgedrückt: Dies ist ein Plugin des Typs IHelloService.

In diesen beiden Projekten kann man machen was man möchte. Hier muss man nur die Referenz auf die HelloMEF.App wegen des Interfaces machen. Ansonsten kennen sich die Applikationen nicht!

Plugin Ordner

Damit die Applikation überhaupt die beiden Plugins kennt, werfen wir beide DLLs in einen eigenen Ordner namens "PlugIns":

image

HelloMEF.App – HelloProgram:

    public class HelloProgram
    {
        [Import(typeof(IHelloService))]
        public List<IHelloService> Services { get; set; }

        public HelloProgram()
        {
	...
        }

        public void WriteHelloGreetings()
        {
            Console.WriteLine();
            Console.WriteLine("Writing Greetings...");

            foreach (IHelloService srv in Services)
            {
                Console.WriteLine(srv.GetHelloMessage());
            }

            Console.WriteLine("... powered by MEF");
        }
    }

In der "HelloProgram" Klasse haben wir eine Liste an "IHelloServices", welches mit dem "Import" Attribute aus dem MEF Namensraum dekoriert ist.

Das bedeutet: Ich nehme alles vom Typen IHelloService auf.

In unserer Ausgabe iterieren wir einfach über diese Liste und rufen die GetHelloMessage auf.

HelloMEF.App – Plugins suchen und finden:

        public HelloProgram()
        {
            this.Services = new List<IHelloService>();

            if (!Directory.Exists("PlugIns"))
            {
                Directory.CreateDirectory("PlugIns");
            }

            AggregatingComposablePartCatalog catalog = new AggregatingComposablePartCatalog();
            catalog.Catalogs.Add(new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
            catalog.Catalogs.Add(new DirectoryPartCatalog("PlugIns"));

            CompositionContainer container = new CompositionContainer(catalog.CreateResolver());
            container.AddPart(this);
            container.Compose();
        }

Wir schauen erstmal nach dem "PluginIns" Verzeichnis und erstellen es wenn nötig. Jetzt folgt pure MEF-Action.

Die PlugIns werden in Katalogen verwaltet. Unserem Katalog sagen wir hier, dass es nach Plugins in dieser Assembly suchen soll:

            catalog.Catalogs.Add(new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));

Und das es auch ein Verzeichnis überwachen soll:

            catalog.Catalogs.Add(new DirectoryPartCatalog("PlugIns"));

Es gibt auch die Möglichkeit das Verzeichnis überwachen zu lassen, sodass man zur Laufzeit Plugins hinzufügen könnte:

image

Durch den Container sagen wir MEF, dass wir hier eine Pluginschnittstelle haben (die Liste mit dem "Import" Attribut) und am Ende geben wir den "Compose" Befehl.

HelloMEF.App – Plugins in derselben Assembly:

namespace HelloMEF.App
{
    public class HelloProgram
    {
	...
    }

    [Export(typeof(IHelloService))]
    public class MEFHelloService : IHelloService
    {
	...
    }

}

Die Plugins können auch in derselben Assembly stehen, MEF findet es durch den "AttributeAssemblyPartCatalog" ebenfalls.

Das Ergebnis:

image

 [ Download Source Code ]


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

2 Responses

  1. Hallo,

    ich habe Probleme, das simple Beispiel geht, aber wenn ich zwei Interfaces in einer Assembly definiere, dann eine Klasse schreibe, die die beiden Interfaces implementiert und Exportiert, und in der anderen Assembly mit einem DirectoryPartCatalog arbeite, findet er nichts zusammen …..

    Auf welcher Leitung stehe ich da?

    DANKE

    Gruß

    Gerhard

    Reply
  2. ??????????????????????????????????????????

    Wer hat jetzt die korrekte Version. Auf meiner verzweifelten Suche nach einer Lösung fand ich folgendes Beispiel:

    1 public class Program
    2 {
    3 static void Main(string[] args)
    4 {
    5 var consumer = new Consumer(); // Objekt mit Imports
    6
    7 var catalog = new AssemblyComponentCatalog();
    8 catalog.AddAssembly(Assembly.GetExecutingAssembly());
    9
    10 var container = new CompositionContainer(catalog.Resolver);
    11 container.AddComponent(consumer);
    12 container.Bind(); // lost die Imports von consumer auf
    13
    14 consumer.ShoutMessage();
    15 }
    16 }

    Sorry, aber ‘mein’ Container versteht weder AddComponent noch Bind.

    Bin ich überhaupt im flaschen Film???

    HILFEEEEE

    Ich habe jedenfalls den Code von Codeplex geladen und referenziere auch die passende dll, also jene, die mir auf Codeplex als die aktuelle ‘verkauft’ wurde.
    Das habe ich ausführlich und mehrmals überprüft, um nicht Opfer irgend einer Trivialität zu werden ….

    Gruß

    Gerhard

    Reply

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook