18.03.2009, 15:27 Uhr

Das neue Managed Extensibility Framework (MEF)

Das kommende .NET 4.0 wird mit dem Managed Extensibility Framework (MEF) eine Bibliothek mit einer standardisierten Infrastruktur für die Entwicklung von erweiterbaren Anwendungen enthalten. Jeder Entwickler, der bereits eine erweiterbare Anwendung implementiert hat weiss, dass die Implementierung einer solchen Infrastruktur zwar notwendig, aber leider auch sehr mühsam ist, so dass mit dem MEF eine spürbare Vereinfachung für die Entwicklung von erweiterbaren Anwendungen einhergehen sollte.
RockMelt will sich als sozialer Browser etablieren
Das neue Managed Extensibility Framework (MEF).NET 4.0, Composition, Extensibility und Plug-In ModelDas kommende .NET 4.0 wird mit dem Managed Extensibility Framework (MEF) eine Bibliothek mit einer standardisierten Infrastruktur für die Entwicklung von erweiterbaren Anwendungen enthalten. Jeder Entwickler, der bereits eine erweiterbare Anwendung implementiert hat weiß, dass die Implementierung einer solchen Infrastruktur zwar notwendig, aber leider auch sehr mühsam ist, so dass mit dem MEF eine spürbare Vereinfachung für die Entwicklung von erweiterbaren Anwendungen einhergehen sollte.von Damir DobricGutes Design liegt im TrendEine gute Anwendung entspricht, gewollt oder nicht, einigen in der Praxis bewährten Prinzipien. Manche Architekten und erfahrene Entwickler versuchen bewusst, diese bewährten Prinzipien (Patterns) in ihren Anwendungen umzusetzen. Einige Patterns, die die Erweiterbarkeit einer Anwendung ermöglichen, sind unter anderem Open Closed Principal [1], Liskov Substitution Principal [2], Dependency of Inversion Control [3] und Design By Contract [4]. Eine wichtige Forderung an eine erweiterbare Anwendung ist, dass sie aus austauschbaren Modulen besteht. Hier ein sehr einfaches Beispiel:ComponentInstance inst = new ComponentInstance (); Es ist nicht möglich, die Komponente ComponentInstance ohne eine Änderung am Quelltext auszutauschen. Ein besserer Ansatz könnte wie folgt aussehen: IComponent proxy = SomeFactory.GetComponentInstance (...); Dieses Beispiel folgt dem "Design by Contract Pattern" und bildet die Grundlage für die Implementierung der Patterns "Inversion of Control", "Substitute Prinzip" usw. Vereinfacht formuliert geht es darum, den new-Operator zu eliminieren. Die Instanz proxy liefert ein Objekt, welches durch IComponent definiert wird. Welches Objekt (Implementierung) sich dahinter verbirgt spielt keine Rolle, so dass diese Komponente gegen eine andere Implementierung ausgetauscht werden kann, ohne dass die Instanzierung angepasst werden muss.Modularität und ErweiterbarkeitDas letzte Beispiel setzt mit der SomeFactory-Klasse auf eine Art "Framework", das den Aufbau einer modularen Anwendung ermöglicht. Microsoft stellt mit dem neuen Managed Extensibility Framework (MEF) ein solches Framework zur Verfügung, das aktuell unter CodePlex [5] als Open Source-Projekt zur Verfügung steht, und das gleichzeitig Teil des kommenden .NET Framework 4.0 sein soll.Was steckt hinter MEF?MEF ist eine Bibliothek, die das Problem der Erweiterbarkeit einer Anwendung zur Laufzeit lösen soll. Sie bietet u.a. das Ermitteln (engl. "discovery") von Typen, das Erzeugen von Instanzen und andere "Composition-Fähigkeiten". Abbildung 1 stellt die wichtigsten Bestandteile zusammen. Die wichtigsten Module im Kern der Bibliothek sind Catalog und CompositionContainer. Catalog kontrolliert das Laden von Komponenten, während CompositionContainer Instanzen erzeugt und diese an die entsprechenden Variablen bindet. Parts sind Objekte, die vom Typ Export oder Import sein können. Exports sind die Komponenten, die geladen und instanziert werden sollen. Imports sind die Variablen, an die die Instanzen der Komponenten gebunden werden. Bezogen auf das kleine Beispiel wäre die Komponente Component1 ein Export, die Variable proxy vom Type IComponent, welche die Instanz dieser Komponente aufnehmen wird, ein Import. Um eine Komponente als Part zu definieren, genügt das entsprechende Attribut:[Export] class Component1 : IComponent{} . . . [Import] IComponent proxyDas schöne am MEF ist, dass die Instanzierung automatisch mit Hilfe von Catalog und Container erfolgt. Abbildung 1: Ein Überblick über die Komponenten des Managed Extensibility FrameworksListing 1 zeigt ein "Hello, World"-Beispiel für das MEF. Die Run-Methode zeigt, wie das MEF-Framework gestartet wird. In diesem Beispiel ist es lediglich wichtig, dass wenn die Run-Methode mit ihrer Ausführung fertig ist, die Variable SingleObject eine Instanz von SampleComponent1 enthält. In diesem trivialen Beispiel scheint das MEF möglicherweise nicht besonders hilfreich zu sein. Da in einer realen Anwendung sehr viele Variablen wie SingleObject (Imports) überall im Code verteilt sind und die kompatiblen Exports-Komponenten (wie SampleComponent1) quer über verschiedene Assemblies verteilt sein können, kann durch den Einsatz des MEF eine deutlich Ersparnis an Codezeilen resultieren. Die Rolle des KatalogsEin Katalog bestimmt im MEF die Art und Weise wie Komponenten geladen werden und legt fest von wo sie geladen werden. Die Komponenten sind entweder in einer gemeinsamen Assembly enthalten, werden durch eine oder mehrere Assemblies statisch referenziert oder werden durch eine oder mehrere Assemblies dynamisch geladen. Um alle Möglichkeiten zu unterstützen bietet das MEF von Haus aus fertige Katalog-Klassen an. Jede dieser Klassen ist für das Laden von Komponenten zuständig:AssemblyCatalogTypeCatalogDirectoryCatalog AggregatingCatalogAssemblyCatalog lädt Parts (Imports und Exports) aus einer angegebenen Assembly. TypeCatalog ermöglicht die "Composition" von explizit gegebenen Typen. DirectoryCatalog lädt Assemblies-Parts aus dem angegebenen Verzeichnis. Darüber hinaus reagiert der Catalog (wenn spezifiziert) auf Veränderungen wie z.B. das Hinzufügen neuer Assemblies im angegebenen Verzeichnis. AggregationCatalog kombiniert unterschiedliche Kataloge.Der Composing-ProzessIm "Hello, World"-Beispiel wurden sog. CompositionContainer und CompositionBatch verwendet Ein CompositionContainer bündelt alle Kataloge und startet einen sog. Composing-Prozess, der in allen Katalogen nach Parts sucht und die Exports an Imports bindet.Das folgende Beispiel zeigt wie der Container mit einem Catalog erzeugt wird: var catalog = new AggregateCatalog();...CompositionContainer container = new CompositionContainer(catalog);Neben Katalogen spielt die sog. CompositionBatch-Klasse eine wichtige Rolle im Composing-Prozess. Da das MEF die Composition während der Laufzeit unterstützt, muss es eine Möglichkeit geben, Parts zur Laufzeit zu entfernen oder hinzufügen. Für diese Aufgabe ist die Klasse CompositionBatch zuständig: var batch = new CompositionBatch();batch.AddPart(this);batch.RemovePart(obj1);container.Compose(batch); Das Binden von KomponentenDie Möglichkeiten der Bindung zwischen einzelnen Parts im Rahmen der MEF-Infrastruktur sind vielfältig und mächtig. Im einfachsten Fall wird ein Export-Part an einen Import-Part gebunden, wie dies am Beispiel der Bindung von Component1 an die Variable Proxy gezeigt wurde. Das funktioniert so lange die Komponente (Component1) vom gleichen Typ wie die Variable proxy ist. Dabei ist es nicht erlaubt mehrere Export-Parts an eine Variable zu "composen". Zum Beispiel könnte es sein, dass zwei Kataloge jeweils einen Export-Part vom erwarteten Typ IComponent implementieren. Um solche Fälle besser kontrollieren zu können, ist es möglich und empfehlenswert eine Art von Vertrag (Contract) zu verwenden:[Export("Part1")]public class SampleComponent1 : ISample1[Export("Part2")] public class SampleComponent1 : ISample1 Die Importseite würde analog wie folgt aussehen: [Import("Part1")] public ISample1 SingleObject { get; set; }[Import("Part2")] public ISample1 SingleObject { get; set; } Häufig ist es notwendig mehrere Parts (z.B. AddIns) in eine Liste laden:[Import("AddIn")] public IEnumerable! VIDEO ! Unterstützung von MetadatenNeben einer einfachen Bindung von Parts gibt es die Möglichkeit, Parts mit Metadaten zu versehen. Die Klasse ComponentWithMetadata exportiert im nächsten Beispiel untypisiert zwei Eigenschaften Position und Priority:[Export("http://daenet.eu/mef/MetadataSample")] [ExportMetadata("Position", "Left")] [ExportMetadata("Priority", 1)]class ComponentWithMetadata{} In einer komplexen Anwendung bietet es sich an die typisierten Metadaten zu verwenden. In diesem Falle muss eine Attribut-Klasse implementiert werden, welche die Metadaten repräsentiert. Die typisierte Variante des vorherigen Beispiels würde damit wie folgt aussehen: [MetadataAttribute] [AttributeUsage(AttributeTargets.Class)] public class DockingCapabilitiesAttribute : Attribute {public Position Position { get; set; }public int Priority{ get; set; } }Der Type Position ist eine Enumeration, die aus Platzgründen nicht dargestellt werden kann. Ein Export-Part würde wie folgt definiert werden:[Export("http://daenet.eu/mef/StronglyTypedMetadataSample")] [DockingCapabilities(Position = Position.Button, Priority = 1)] class ComponentWithTypeMetadata : IMetadata...FazitEs ist sehr erfreulich, dass von Microsoft mit dem Managed Extensibility Framework (MEF) endlich eine "Composition-Bibliothek" für das .NET Framework standardisiert wurde. MEF wird von einigen Microsoft-Produktteams bereits eingesetzt und es ist daher zu erwarten, dass in Zukunft viele Microsoft Produkte diese Infrastruktur als die Basis für Third-Party-AddIns verwenden. Die prominentesten Beispiele sind künftige Versionen von Expression Blend, der Power Shell und Visual Studio 2010.Listing 1: Ein "Hello, World" mit dem MEFusing System;using System.ComponentModel.Composition;using MefSample.Components.Sample1;using System.ComponentModel.Composition.Hosting;namespace MefSample.Samples{[Export("http://daenet.eu/mef/Sample1")]public class SampleComponent2 : ISample1, INotifyImportSatisfaction { ]public class HelloWorldSample1{[Import("http://daenet.eu/mef/Sample1")]public ISample1 SingleObject { get; set; }public void Run(){AssemblyCatalog catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);var batch = new CompositionBatch();batch.AddPart(this);container.Compose(batch);Console.WriteLine(SingleObject.ToString());}}}Links[1] Open Closed Principal: http://www.objectmentor.com/resources/articles/ocp.pdf[2] Liskov Substitution Principal: http://www.objectmentor.com/resources/articles/lsp.pdf[3] Dependency of Inversion Principal: http://www.objectmentor.com/resources/articles/dip.pdf[4] Design By Contract Principal: http://en.wikipedia.org/wiki/Design_by_contract[5] Managed Extensibility Framework im CodePlexhttp://www.codeplex.com/MEF[6] Häufig mit MEF verwechselt: Windsor Container im Castle Projekt: http://www.castleproject.org/container/index.htmlPeter Monadiemi


Das könnte Sie auch interessieren