Dieser Artikel wurde maschinell übersetzt.

Innovation

Anwendungserweiterbarkeit: MEF vs.IoC

Dino Esposito

image: Dino EspositoEs ist eine interessante neue Komponente in der Microsoft NET Framework 4 speziell entwickelt, um eine wirksame Antwort auf eine immergrüne Frage stellen. Wie würden Sie erweiterbaren Anwendungen, die zur Laufzeit entdecken können alle Teile, die sie gemacht sind zu schreiben?

Wie Glenn Block in seinem Februar 2010-Artikel "Building zusammenstellbar Anwendungen in erklärt.NET-4 mit verwalteten Erweiterbarkeitsframework")MSDN.Microsoft.com/magazine/ee291628), die verwaltete Erweiterbarkeit Framework (MEF) kann verwendet werden, um erstellen zusammensetzbare und plug-in-basierte Anwendungen zu optimieren.Als einer, der gestartet hat, das Problem wieder im Jahr 1994 behandelt (Ja, es war einer meiner ersten echten Herausforderungen als Entwickler), ich definitiv freue mich über vorgeschlagenen Lösungen in diesen Problembereich.

Das MEF erfordert keine kaufen, herunterladen und zusätzlichen Bibliotheken verweisen, und es bietet eine einfache Programmierschnittstelle, da er konzentrierte sich auf die Lösung dieses Problems Allgemein, Drittanbieter-Erweiterbarkeit von vorhandenen Anwendungen.Glenn's Artikel ist eine ausgezeichnete Einführung in die MEF und sollten als erforderliche Lektüre angesehen werden, wenn Sie, über plug-in-basierte Anwendungen Vorhaben.

In diesem Artikel werde ich die erforderlichen Schritte zum Erstellen einer extensible Anwendung mit dem MEF als zugrunde liegenden kleben die Hauptteil und externe Teile der Anwendung das Zusammenhalten durchlaufen.

Vom IoC zum MEF und zurück

Bevor ich die Beispielanwendung zu erhalten, jedoch ich möchte einige Gedanken über die MEF und eine weitere beliebte Familie von Frameworks: Inversion of Control (IoC).

Kurz gesagt, ist es richtig zu sagen, die die Funktionalität von der MEF und eines typischen IoC-Frameworks überlappen, jedoch nicht übereinstimmen.Mit den meisten IoC-Frameworks können Sie Aufgaben ausführen, die nicht nur von der MEF unterstützt werden.Sie konnte wahrscheinlich funktionalsten IoC-Container einsetzen und mit einigen Aufwand selbst einige MEF-spezifische Funktionen zu emulieren.In light of this, the question that I’m frequently asked when I mention the MEF in classes and everyday work is: What’s the difference between the MEF and an IoC tool?Und wann brauche ich wirklich den MEF?

Meine Gedanken besteht darin, dass im Kern der MEF ein IoC-Framework, die direkt integriert ist der.NET Framework.Es ist heute nicht so leistungsfähig wie viele beliebte IoC-Frameworks, aber er die grundlegenden Aufgaben der typischen IoC-Container sehr gut ausführen kann.

IoC-Frameworks haben heute drei typische Funktionen.Erstens können fungiert als Factory für ein Diagramm von Objekten und gehen Sie durch die Kette der Objekt-Beziehungen und Abhängigkeiten zum Erstellen einer Instanz eines beliebigen Typs erforderlich und registriert.Zweitens ein IoC-Framework die Lebensdauer des erstellten Instanzen verwalten und Zwischenspeichern und pooling-Funktionen bieten.Dritte, die meisten IoC-Frameworks Unterstützung abfangen und Angebot zum dynamischen Proxys, um Instanzen eines bestimmten Typs zu erstellen, sodass Entwickler vor-können und Nachbearbeitung die Ausführung von Methoden.Ich behandelt abfangen in Unity 2.0 im Januar)MSDN.Microsoft.com/magazine/gg535676).

MEF, in einer Weise kann als die Factory ein Diagramm von Objekten, dienen, was bedeutet, dass es erkennen und behandeln Member einer Klasse, die zur Laufzeit aufgelöst werden müssen.Das MEF bietet auch minimale Unterstützung für das Zwischenspeichern von Instanzen, was bedeutet, dass einige Zwischenspeicherfunktionen vorhanden, aber sie nicht als in einigen anderen IoC-Frameworks wie funktionalsten sind.Schließlich in der Version, die im Lieferumfang der.NET Framework-4, fehlen die MEF Interception-Funktionen vollständig aus.

Andererseits sollten Sie bei der MEF verwenden?Wenn Sie noch nie ein IoC-Framework verwendet haben und nur muss den Entwurf Ihres Systems bereinigen, indem eine Bit der Abhängigkeit Injection und dann das MEF eine einfache Start sein kann.Solange Sie Ihre Ziele mit es schnell erreichen können, ist das MEF vorzuziehen, ein IoC-Framework.

Andererseits, wenn Sie arbeiten mit einem oder mehreren IoC-Frameworks Jahren warst und jedes Bit von diese Funktionen können squeeze, liegt dann vermutlich kein, mit denen die MEF außer vielleicht die Möglichkeit, verschiedene Arten von Katalogen finden Sie übereinstimmende Typen Scannen erhalten werden kann.Es sei darauf hingewiesen, jedoch, einige IoC-Frameworks wie z. B. "StructureMap" ()structuremap.NET/StructureMap/ScanningAssemblies.htm) bieten bereits zum Scannen von Verzeichnissen und Assemblys suchen für bestimmte Typen oder Implementierungen von Schnittstellen zugewiesen.Mit dem MEF ist dies wahrscheinlich einfacher und direkter als mit "StructureMap" (und einige andere) zu tun.

Zusammenfassend lässt sich sagen ist die erste Frage beantworten, ob Sie für die Allgemeine Erweiterbarkeit interessieren.Wenn Ja, dann der MEF angesehen werden muss – vielleicht in ein IoC-Tool, wenn auch Abhängigkeiten, Singletons und Abfangen behandelt werden müssen.Wenn die Antwort lautet Nein, dann am besten verwendet ein IoC-Framework es sei denn, Sie haben grundlegende Anforderungen, die das MEF auch behoben werden kann.Alles im allem, das MEF ist ein IoC-Framework vorzuziehen, da es direkt in integriert ist der.NET Framework und Sie müssen zusätzlichen Abhängigkeiten zu ergreifen.

Das MEF und erweiterbaren Anwendungen

Während der MEF im Gebäude einer extensible Anwendung hilft, entwirft der besonders schwierige Teil der Auftrag die Anwendung für die Erweiterbarkeit.Dies ist der Entwurf und hat wenig mit dem MEF, IoC oder andere Technologien zu tun.Sie müssen insbesondere, welche Teile der Anwendung herausfinden Sie Plug-ins zur Verfügung stellen möchten.

Ein plug-in ist oft ein visuelles Element und muss für die Interaktion mit der Benutzeroberfläche der Hauptanwendung, hinzufügen oder erweitern Menüs, Bereiche zu erstellen, Anzeigen von Dialogfeldern, oder sogar hinzufügen oder vergrößern Sie die Haupt-Fenster.Je nachdem, wie Sie die Plug-ins für Ihre spezielle Anwendung envision die Menge an Informationen für Plug-Ins freigeben bestehen aus nur Geschäftsdaten (im Wesentlichen ein Segment des aktuellen Zustand der Anwendung) oder ein Verweis auf die visuelle Elemente wie Behälter, Menüs, Symbolleisten und sogar bestimmte Steuerelemente.Sie diese Informationen in einer Datenstruktur zu gruppieren und nach unten auf das plug-in Initialisierungszeit übergeben.Auf der Grundlage dieser Informationen sollte das plug-in möglicherweise eigene Benutzeroberfläche anpassen und eigene zusätzlichen benutzerdefinierten Logik implementieren.

Als Nächstes kommt die Schnittstelle für die Plug-ins.Die Schnittstelle hängt die Injektion-Punkte, die Sie in der Hauptanwendung identifiziert haben."Injection Point" meine ich die Stellen im Code der Anwendung, aus denen Sie Plug-ins, die Möglichkeit kick und Betrieb von aufrufen würden.

Ein Beispiel für eine Injektion Point sollten Sie Windows Explorer.Wie Sie wissen vielleicht, können Windows Explorer, seine Benutzeroberfläche mittels Shellerweiterungen zu erweitern.Diese Plug-ins unter ganz bestimmten Augenblicken aufgerufen werden – z. B. wenn der Benutzer klickt auf die Eigenschaften einer ausgewählten Datei anzuzeigen.Als Architekt für die Anwendung es Ihrer Verantwortung, diese Punkte Injektion zu identifizieren und welche Daten übergeben werden sollen registriert-Plug-ins zu diesem Zeitpunkt.

Sobald alle Aspekte des Entwurfs von gelöscht wurde, können Sie für Frameworks suchen um, die die Aufgabe der Erstellung plug-in-basierte Anwendungen vereinfachen können.

Eine Beispielanwendung Plug-In-basierte

Selbst eine einfache Anwendung, wie z. B. "Suche die Zahl" kann gezieltere und funktional ansprechend über Plug-Ins vorgenommen werden.Abbildung 1 zeigt die grundlegende Benutzeroberfläche der Anwendung.Möglicherweise möchten ein separates Projekt so definieren Sie das SDK der Anwendung zu erstellen.Es wird eine Bibliothek Klasse sein, Sie definieren alle Klassen und Schnittstellen zum Implementieren von Plug-ins erforderlich.Abbildung 2 zeigt ein Beispiel dafür.

image: The Simple Sample Application

Abbildung 1 die einfache Beispielanwendung

Abbildung 2 Definitionen für die SDK-Anwendung

public interface IFindTheNumberPlugin {
  void ShowUserInterface(GuessTheNumberSite site);
  void NumberEntered(Int32 number);
  void GameStarted();
  void GameStopped();
}
public interface IFindTheNumberApi {
  Int32 MostRecentNumber { get; }
  Int32 NumberOfAttempts { get; }
  Boolean IsUserPlaying { get; }
  Int32 CurrentLowerBound { get; }
  Int32 CurrentUpperBound { get; }
  Int32 LowerBound { get; }
  Int32 UpperBound { get; }
  void SetNumber(Int32 number);
}
public class FindTheNumberFormBase : Form, IFindTheNumberApi {
  ...
}

Alle Plug-ins sind erforderlich, um die IFindTheNumberPlugin-Schnittstelle implementieren. Das Hauptanwendungsformular erbt von der angegebenen Form-Klasse, die eine Liste der öffentlichen Hilfsmember zum Übergeben von Informationen zu Plug-ins definiert.

Wie Sie aus IFindTheNumberPlugin erraten können, werden registrierten Plug-Ins aufgerufen, wenn die Anwendung Ihrer Benutzeroberfläche anzeigt, bei der der Benutzer einen neuen Versuch, die Anzahl zu erraten und das Spiel gestartet und beendet wird. GameStarted und GameStopped sind nur die Benachrichtigungsmethoden und müssen keine Eingaben. NumberEntered ist eine Benachrichtigung, die die Anzahl den Benutzer genau eingegeben und übermittelt für einen neuen Versuch bringt. Schließlich wird ShowUserInterface aufgerufen, wenn das plug-in im Fenster erscheinen muss. In diesem Fall ein Objekt übergeben wird, im Sinne der Begriffsbestimmung in Abbildung 3.

Abbildung 3 das Standortobjekt für Plug-Ins

public class FindTheNumberSite {
  private readonly FindTheNumberFormBase _mainForm;
  public FindTheNumberSite(FindTheNumberFormBase form) {
    _mainForm = form;
  }
  public T FindElement<T>(String name) where T:class { ... }
  public void AddElement(Control element) { ... }
  public Int32 Height {
    get { return _mainForm.Height; }
    set { _mainForm.Height = value; }
  }
  public Int32 Width { ... }
  public Int32 NumberOfAttempts { ... }
  public Boolean IsUserPlaying { ... }
  public Int32 LowerBound { ... }
  public Int32 UpperBound { ... }
  public void SetNumber(Int32 number) { ... }
}

Das Site-Objekt repräsentiert der Kontaktpunkt zwischen dem plug-in und die Host-Anwendung. Das plug-in muss einige Einblick des Staates Host und muss auch die Host-Benutzeroberfläche zu ändern, aber nie erlangt er Kenntnis der internen Details der Host. Deshalb können Sie ein Zwischenstandort-Objekt (Teil der Baugruppe SDK) erstellen, die Plug-in-Projekte verweisen müssen.

Ich die Implementierung von den meisten Methoden in der Kürze halber weggelassen Abbildung 3, aber der Konstruktor das Objekt erhält einen Verweis auf der Anwendungsverzeichnis im Hauptfenster und verwenden Hilfsmethoden in Abbildung 2 (die vom main Window-Objekt verfügbar gemacht) es lesen und Schreiben von Zustand und die visuellen Elemente der Anwendungsverzeichnis. Beispielsweise zeigt der Member Höhe wie das plug-in kann lesen und schreiben die Höhe des Fensters Host.

Insbesondere kann die FindElement-Methode das plug-in (in der Beispielanwendung) ein bestimmtes visuelles Element im Formular abrufen. Es wird davon ausgegangen, dass Sie einige technische Details zum Zugriff auf bestimmte Container wie z. B. Symbolleisten, Menüs und ähnlichem als Teil Ihrer SDK bieten. In einer einfachen Anwendung hat Sie Dokuments die ID der physischen Steuerelemente angenommen. Hier ist die Implementierung von FindElement:

public T FindElement<T>(String name) where T:class {
  var controls = _mainForm.Controls.Find(name, true);
  if (controls.Length == 0)
    return null;
  var elementRef = controls[0] as T;
  return elementRef ?? null;
}
With the design of the application’s extensibility model completed, we’re now ready to introduce 
the MEF.

Definieren von Einfuhren für Plug-ins

Der Hauptfenster der Anwendung wird sicherlich eine Eigenschaft verfügbar machen, die alle aktuell registrierten Plug-Ins aufgeführt sind. Im Folgenden finden Sie ein Beispiel:

public partial class FindTheNumberForm : 
  FindTheNumberFormBase {
  public FindTheNumberForm() {
    InitializeMef();
    ...
 }
 [ImportMany(typeof(IFindTheNumberPlugin)]
 public List<IFindTheNumberPlugin> Plugins { 
    get; set; 
  }
  ...
}

Beim Initialisieren der MEF bedeutet das Vorbereiten der Komposition Container angeben die Kataloge, die Sie verwenden möchten und die optionale Export-Anbieter. Eine allgemeine Lösung für plug-in-basierte Anwendungen wird aus einem festen Ordner Plug-ins geladen. Abbildung 4 den Startcode, der das MEF in meinem Beispiel zeigt.

Abbildung 4 beim Initialisieren der MEF

private void InitializeMef() {
  try {
    _pluginCatalog = new DirectoryCatalog(@"\My App\Plugins");
    var filteredCatalog = new FilteredCatalog(_pluginCatalog, 
      cpd => cpd.Metadata.ContainsKey("Level") && 
      !cpd.Metadata["Level"].Equals("Basic")); 
    // Create the CompositionContainer with the parts in the catalog
    _container = new CompositionContainer(filteredCatalog);
    _container.ComposeParts(this);
  }
  catch (CompositionException compositionException) {
    ...
  }
  catch (DirectoryNotFoundException directoryException) { 
    ...
  }
}

Sie verwenden eine DirectoryCatalog zum Gruppieren von verfügbaren Plug-Ins und verwenden Sie die FilteredCatalog-Klasse (also nicht in der MEF, aber ein Beispiel in der MEF-Dokumentation unter Bit.LY/gf9xDK) einige der ausgewählten Plug-in-Filtern. Insbesondere können Sie anfordern, dass alle loadable Plug-ins ein Metadatenattribut verfügen, die die Ebene angibt. Das Attribut fehlt, wird das plug-in ignoriert.

Der Aufruf von ComposeParts hat die Auswirkung des Füllens des Plugins-Eigenschaft der Anwendung. Die nächste Schritt ist nur aus den verschiedenen Punkten der Injektion-Plug-ins aufrufen. Das erste Mal Aufrufen von Plug-ins ist richtig, nachdem die Anwendung geladen wird, um ihnen eine Möglichkeit zum Ändern der Benutzeroberfläche zu verleihen:

void FindTheNumberForm_Load(Object sender, EventArgs e) {
  // Set up UI
  UserIsPlaying(false);
  // Stage to invoke plugins
  NotifyPluginsShowInterface();
}
void NotifyPluginsShowInterface() {
  var site = new FindTheNumberSite(this);
  if (Plugins == null)
    return;
  foreach (var p in Plugins) {
    p.ShowUserInterface(site);
  }
}

Ähnliche Aufrufe werden im Ereignis Handler angezeigt, die signalisieren, wenn der Benutzer gerade ein neues Spiel starten, beenden Sie das aktuelle Spiel oder soeben einen neuen Versuch erstellten, geheimnisvolle Zahl zu raten.

Schreiben Sie eine Beispiel-Plug-in

Ein plug-in ist einfach eine Klasse, die Ihre Anwendung Erweiterbarkeit-Schnittstelle implementiert. Eine interessante-Plug-In für die Anwendung in Abbildung 1 ist eine mit der Anzahl der Versuche des Benutzers bisher angezeigt. Die Anzahl der Versuche durch die Geschäftslogik der Anwendung nachverfolgt wird, und es wird über das Standortobjekt-Plug-ins ausgesetzt. Führen Sie ein plug-in muss lediglich eine eigene Benutzeroberfläche vorbereiten, binden Sie es an die Anzahl der Versuche und Anfügen an das Hauptfenster.

Plug-ins der Beispielanwendung erstellt neue Steuerelemente in der Benutzeroberfläche des Hauptfensters. Abbildung 5 zeigt ein Beispiel-Plug-in.

Abbildung 5 den Leistungsindikator-Plug-in

[Export(typeof(IFindTheNumberPlugin))]
[PartMetadata("Level", "Advanced")]
public class AttemptCounterPlugin : IFindTheNumberPlugin {
  private FindTheNumberSite _site;
  private Label _attemptCounterLabel;
  public void ShowUserInterface(FindTheNumberSite site) {
    _site = site;
    var numberToGuessLabelRef = _host.FindElement<Label>("NumberToGuess");
    if (numberToGuessLabelRef == null)
      return;
    // Position of the counter label in the form 
    _attemptCounterLabel = new Label {
      Name = "plugins_AttemptCounter",
      Left = numberToGuessLabelRef.Left,
      Top = numberToGuessLabelRef.Top + 50,
      Font = numberToGuessLabelRef.Font,
      Size = new Size(150, 30),
      BackColor = Color.Yellow,
      Text =  String.Format("{0} attempt(s)", _host.NumberOfAttempts)
    };
    _site.AddElement(_attemptCounterLabel);
  }
  public void NumberEntered(Int32 number = -1) {
    var attempts = _host.NumberOfAttempts;
    _attemptCounterLabel.Text = String.Format("{0} attempt(s)", attempts);
    return;
  }
  public void GameStarted() {
    NumberEntered();
  }
  public void GameStopped() {
  }
}

Das plug-in erstellt ein neues Label-Steuerelement und legt ihn direkt unter einem vorhandenen Element der Benutzeroberfläche.Wenn das plug-in die Benachrichtigung erhält, dass eine neue Nummer eingegeben wurde, wird als Nächstes der Leistungsindikator aktualisiert, und die aktuelle Anzahl der Versuche gemäss den Zustand der Geschäftslogik an.Abbildung 6 zeigt das plug-in in Aktion.

image: The Sample App and a Few Plug-Ins

Abbildung 6 der Beispielanwendung und einige Plug-Ins

Anschließen

Am Ende des Tages ist die sehr schwierige Aufgabe, erweiterbare Anwendungen entwerfen, den Entwurf der Host und die Schnittstelle des Plug-ins.Dies ist eine reine Entwurfsaufgaben und mit der KE-Liste und Benutzeranforderungen zu tun hat.

Bei der Implementierung angeht, müssen Sie jedoch ein paar praktische Aufgaben durchführen, unabhängig von der Plug-in-Schnittstelle, z. B. auswählen, laden und Überprüfen der Plug-ins.Das MEF erhalten Sie in dieser Hinsicht erhebliche Hilfe bei der Vereinfachung der Erstellung des Katalogs der Plug-ins geladen und automatisch laden oben im Wesentlichen die gleiche Weise wie, die ein IoC-Framework tun würden.

Beachten Sie, dass das MEF eine kontinuierliche Entwicklung ist und Sie die neuesten Bits, Dokumentation und Beispielcode am finden MEF.codeplex.com.

Dino Esposito ist der Verfasser von “Programming Microsoft ASP.NET MVC” (Microsoft Press, 2010) und der Mitverfasser von “Microsoft .NET: Architecting Applications for the Enterprise“ (Microsoft Press 2008). Esposito lebt in Italien und ist ein weltweit gefragter Referent für Branchenveranstaltungen.

Dank an den folgenden technischen Experten für die Überprüfung dieses Artikels: Glenn Block