19 November 2008 AddIns, HowTo, MEF, Plugins, System.AddIn Robert Muehsig

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 ]


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de