Basierend auf meinem Post in einem C#-Thread kommt hier mal ein umfangreicherer Post, was dieses Thema betrifft.
Für diesen Tipp werden ein paar Grundlagen wie z. B. die OOP vorausgesetzt.
Das Managed Extensibility Framework
Das Managed Extensibility Framework, kurz MEF, ist ein Bestandteil des .NET-Framework ab Version 4.0. Es ermöglicht das Erstellen von Anwendungen welche durch Plugins erweitert werden können. Für jüngere Frameworks (3.5) kann man sich den Quelltext von CodePlex herunterladen und es in seinem Projekt verwenden.
Mehr Informationen zum MEF findet Ihr dort:
MSDN-Seite zu MEF: msdn.microsoft.com/en-us/library/dd460648.aspx
MEF-Quelltext: mef.codeplex.com
Wikipedia-Artikel: en.wikipedia.org/wiki/Managed_Extensibility_Framework
Okay, dann mal los!
Fangen wir an. Wir wollen eine Anwendung erstellen, die irgendwo hier einen String bekommt. Dann füttern wir unser Programm mit Plugins, welche diesen String in irgendeiner Weise verarbeiten und einen Boolean zurückgeben, der angibt, ob der Vorgang erfolgreich war.
Wir erstellen ein neues Konsolenprojekt. Meins heißt „MefHostApplication“.
Als Erstes geben wir dem Benutzer die Möglichkeit, etwas einzugeben. Das, was er eingegeben hat, wird dann an unsere Plugins übergeben.
Ich habe so angefangen:
Nun stellen wir uns vor, wie die Funktion aussehen soll, die wir in unseren Plugins verwenden möchten. Wir wollen einen String nehmen und einen Boolean zurückgeben (oben beschrieben).
Das bedeutet, dass der Funktionskopf für Plugins so aussehen muss:
Mit dieser Erkenntnis erstellen wie uns ein Interface, welches genau diese Funktion vorschreibt:
(Achtung, es muss Public sein!)
Jetzt implementieren wir unser erstes Test-Plugin. Dazu erstellen wir ein neues Projekt (Klassenbibliothek). Meins heißt „MefTestPlugin“
Unser erstes Plugin soll einfach nur die Anzahl an Buchstaben der Eingabe ausgeben. Dazu verweisen wir in dem Plugin-Projekt auf das Projekt der Host-Anwendung (bei mir „MefHostApplication“). Anschließend erstellen wir uns eine Klasse, die das IPlugin-Interface implementiert.
Bisher haben wir noch nicht viel von MEF gesehen. Genauer gesagt Garnichts. Alles ist normale objektorientierte Programmierung.
MEF kommt ins Spiel
In unserem Plugin-Projekt fügen wir nun einen Verweis auf die „System.ComponentModel.Composition.dll“ hinzu. Diese befindet sich bei den restlichen Framework-Assemblys.
Nun kleben wir das Export-Attribut an unsere Plugin-Klasse „EingabeLängePlugin“. Das sieht so aus:
Das Export-Attribut sagt MEF, dass es sich um eine Klasse handelt, die von MEF-Instanzen importiert und somit geladen werden kann.
Das war’s auch schon auf der Seite des Plugins. Mehr muss der Plugin-Entwickler nicht mehr machen außer zu kompilieren und die DLL bereitzustellen.
Gehen wir also nun wieder zurück zur Host-Anwendung („MefHostApplication“). Bei diesem Projekt fügen wir ebenfalls einen Verweis auf die „System.ComponentModel.Composition.dll“ hinzu.
Wir packen ein Array vom Typ IPlugin in unsere Anwendung. Bei mir kommt es, weil es sich um eine Konsolenanwendung handelt, nicht in das Hauptmodul, sondern in eine neue Klasse namens „PluginVerbindung“. Man kann es aber überall hinein packen. Dieses Array bekleben wir nun wir dem ImportMany-Attribut aus dem gleichen Namespace wie das Export-Attribut von weiter oben. Das sieht dann so aus:
In diesem Array landen am Ende Instanzen von allen Klassen, die das IPlugin-Interface implementieren und mit dem ExportAttribute versehen wurden.
Um die Plugins zu laden benötigen wir jetzt eine Funktion, die das für uns erledigt. Keine Angst, so schwer ist das nicht.
Im MEF benötigt man immer einen CompositionContainer, welcher genau einen Katalog beinhaltet. Ein Katalog sagt dem CompositionContainer, woher er sich die Plugins holen soll. Es gibt verschiedene Kataloge, die man verwenden kann. Ich benutze hier den DirectoryCalalog. Wie der Name schon sagt, hat der etwas mit einem Verzeichnis zu tun. Er holt sich die Plugins also aus einem Verzeichnis.
Letztendlich ruft man beim CompositionContainer die ComposeParts-Erweiterungsmethode auf. Die Methode nimmt eine Objektinstanz an, in der sich das Array befindet, in das die Plugins hereingeladen werden sollen. Die LadePlugins –Funktion sieht also nun so aus:
Achtung, das Plugin-Verzeichnis muss existieren, sonst bekommt Ihr einen Fehler!
Die ganze PluginVerbindung-Klasse sieht nun so aus:
Diese Klasse verwenden wir nun in unserem Sub Main. Beim Programmstart laden wir die Plugins:
Nun können wir nach der Benutzereingabe durch alle Plugins gehen und nacheinander die Verarbeitungsfunktionen aufrufen:
Das war’s
Jetzt kann man natürlich ein bisschen herumspielen und z. B. den Autornamen in das Interface packen, damit man ausgeben kann, von wem das Plugin erstellt wurde. Generell ist alles möglich, was bei OOP möglich ist. Eurer Kreativität sind also keine Grenzen gesetzt.
Das ganze Host-Programm sieht am Ende so aus:
Spoiler anzeigen
Das Beispielplugin:
Spoiler anzeigen
Hier noch ein paar andere Kataloge, die man verwenden kann:
Hier noch ein Video: youtube.com/watch?v=EFjD1rTcXAQ
Hoffe, Euch hat es geholfen und/oder inspiriert.
nikeee
Für diesen Tipp werden ein paar Grundlagen wie z. B. die OOP vorausgesetzt.
Das Managed Extensibility Framework
Das Managed Extensibility Framework, kurz MEF, ist ein Bestandteil des .NET-Framework ab Version 4.0. Es ermöglicht das Erstellen von Anwendungen welche durch Plugins erweitert werden können. Für jüngere Frameworks (3.5) kann man sich den Quelltext von CodePlex herunterladen und es in seinem Projekt verwenden.
Mehr Informationen zum MEF findet Ihr dort:
MSDN-Seite zu MEF: msdn.microsoft.com/en-us/library/dd460648.aspx
MEF-Quelltext: mef.codeplex.com
Wikipedia-Artikel: en.wikipedia.org/wiki/Managed_Extensibility_Framework
Okay, dann mal los!
Fangen wir an. Wir wollen eine Anwendung erstellen, die irgendwo hier einen String bekommt. Dann füttern wir unser Programm mit Plugins, welche diesen String in irgendeiner Weise verarbeiten und einen Boolean zurückgeben, der angibt, ob der Vorgang erfolgreich war.
Wir erstellen ein neues Konsolenprojekt. Meins heißt „MefHostApplication“.
Als Erstes geben wir dem Benutzer die Möglichkeit, etwas einzugeben. Das, was er eingegeben hat, wird dann an unsere Plugins übergeben.
Ich habe so angefangen:
VB.NET-Quellcode
- Module MainModule
- Sub Main()
- Console.WriteLine("Bitte geben Sie irgendetwas ein:")
- Dim eingabe As String = Console.ReadLine()
- Console.WriteLine("Sie haben eingegeben: " & eingabe)
- Console.WriteLine("Die Eingabe wird nun verarbeitet...")
- Console.WriteLine()
- 'TODO: Eingabe verarbeiten
- End Sub
- End Module
Nun stellen wir uns vor, wie die Funktion aussehen soll, die wir in unseren Plugins verwenden möchten. Wir wollen einen String nehmen und einen Boolean zurückgeben (oben beschrieben).
Das bedeutet, dass der Funktionskopf für Plugins so aussehen muss:
Public Function Verarbeite(ByVal str As String) As Boolean
Mit dieser Erkenntnis erstellen wie uns ein Interface, welches genau diese Funktion vorschreibt:
(Achtung, es muss Public sein!)
Jetzt implementieren wir unser erstes Test-Plugin. Dazu erstellen wir ein neues Projekt (Klassenbibliothek). Meins heißt „MefTestPlugin“
Unser erstes Plugin soll einfach nur die Anzahl an Buchstaben der Eingabe ausgeben. Dazu verweisen wir in dem Plugin-Projekt auf das Projekt der Host-Anwendung (bei mir „MefHostApplication“). Anschließend erstellen wir uns eine Klasse, die das IPlugin-Interface implementiert.
VB.NET-Quellcode
- Imports MefHostApplication
- Public Class EingabeLängePlugin
- Implements IPlugin
- Public Function Verarbeite(ByVal str As String) As Boolean Implements IPlugin.Verarbeite
- Console.WriteLine("Länge der Eingabe: " & str.Length.ToString()) ' Länge des Strings ausgeben
- Return True ' Der Vorgang war erfolgreich, also "True" zurückgeben
- End Function
- End Class
Bisher haben wir noch nicht viel von MEF gesehen. Genauer gesagt Garnichts. Alles ist normale objektorientierte Programmierung.
MEF kommt ins Spiel
In unserem Plugin-Projekt fügen wir nun einen Verweis auf die „System.ComponentModel.Composition.dll“ hinzu. Diese befindet sich bei den restlichen Framework-Assemblys.
Nun kleben wir das Export-Attribut an unsere Plugin-Klasse „EingabeLängePlugin“. Das sieht so aus:
Das Export-Attribut sagt MEF, dass es sich um eine Klasse handelt, die von MEF-Instanzen importiert und somit geladen werden kann.
Das war’s auch schon auf der Seite des Plugins. Mehr muss der Plugin-Entwickler nicht mehr machen außer zu kompilieren und die DLL bereitzustellen.
Gehen wir also nun wieder zurück zur Host-Anwendung („MefHostApplication“). Bei diesem Projekt fügen wir ebenfalls einen Verweis auf die „System.ComponentModel.Composition.dll“ hinzu.
Wir packen ein Array vom Typ IPlugin in unsere Anwendung. Bei mir kommt es, weil es sich um eine Konsolenanwendung handelt, nicht in das Hauptmodul, sondern in eine neue Klasse namens „PluginVerbindung“. Man kann es aber überall hinein packen. Dieses Array bekleben wir nun wir dem ImportMany-Attribut aus dem gleichen Namespace wie das Export-Attribut von weiter oben. Das sieht dann so aus:
In diesem Array landen am Ende Instanzen von allen Klassen, die das IPlugin-Interface implementieren und mit dem ExportAttribute versehen wurden.
Um die Plugins zu laden benötigen wir jetzt eine Funktion, die das für uns erledigt. Keine Angst, so schwer ist das nicht.
Im MEF benötigt man immer einen CompositionContainer, welcher genau einen Katalog beinhaltet. Ein Katalog sagt dem CompositionContainer, woher er sich die Plugins holen soll. Es gibt verschiedene Kataloge, die man verwenden kann. Ich benutze hier den DirectoryCalalog. Wie der Name schon sagt, hat der etwas mit einem Verzeichnis zu tun. Er holt sich die Plugins also aus einem Verzeichnis.
Letztendlich ruft man beim CompositionContainer die ComposeParts-Erweiterungsmethode auf. Die Methode nimmt eine Objektinstanz an, in der sich das Array befindet, in das die Plugins hereingeladen werden sollen. Die LadePlugins –Funktion sieht also nun so aus:
VB.NET-Quellcode
- Public Sub LadePlugins()
- Dim catalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
- ' catalog gibt an, dass die Plugins aus dem Verzeichnis „Plugin“ geladen werden sollen
- Dim container = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
- container.ComposeParts(Me) ' Hier passiert die Magie!
- End Sub
Achtung, das Plugin-Verzeichnis muss existieren, sonst bekommt Ihr einen Fehler!
Die ganze PluginVerbindung-Klasse sieht nun so aus:
VB.NET-Quellcode
- Class PluginVerbindung
- <ImportMany(GetType(IPlugin))> _
- Public Plugins As IPlugin()
- Public Sub LadePlugins()
- Dim catalog As DirectoryCatalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
- Dim container As CompositionContainer = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
- container.ComposeParts(Me)
- End Sub
- End Class
Diese Klasse verwenden wir nun in unserem Sub Main. Beim Programmstart laden wir die Plugins:
Nun können wir nach der Benutzereingabe durch alle Plugins gehen und nacheinander die Verarbeitungsfunktionen aufrufen:
Das war’s
Jetzt kann man natürlich ein bisschen herumspielen und z. B. den Autornamen in das Interface packen, damit man ausgeben kann, von wem das Plugin erstellt wurde. Generell ist alles möglich, was bei OOP möglich ist. Eurer Kreativität sind also keine Grenzen gesetzt.
Das ganze Host-Programm sieht am Ende so aus:
VB.NET-Quellcode
- Imports System.ComponentModel.Composition
- Imports System.ComponentModel.Composition.Hosting
- Module MainModule
- Sub Main()
- Dim verbindung As New PluginVerbindung()
- verbindung.LadePlugins()
- Console.WriteLine("Bitte geben Sie irgendetwas ein:")
- Dim eingabe As String = Console.ReadLine()
- Console.WriteLine("Sie haben eingegeben: " & eingabe)
- Console.WriteLine("Die Eingabe wird nun verarbeitet...")
- Console.WriteLine()
- For Each plugin As IPlugin In verbindung.Plugins
- plugin.Verarbeite(eingabe) ' Gibt zurück, ob Vorgang erfolgreich war; der Boolean ist hier unverwendet
- Next
- Console.ReadKey()
- End Sub
- End Module
- Class PluginVerbindung
- <ImportMany(GetType(IPlugin))> _
- Public Plugins As IPlugin()
- Public Sub LadePlugins()
- Dim catalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
- Dim container = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
- container.ComposeParts(Me)
- End Sub
- End Class
- Public Interface IPlugin
- Function Verarbeite(ByVal str As String) As Boolean
- End Interface
Das Beispielplugin:
VB.NET-Quellcode
- Imports System.ComponentModel.Composition
- Imports MefHostApplication
- <Export(GetType(IPlugin))>
- Public Class EingabeLängePlugin
- Implements IPlugin
- Public Function Verarbeite(ByVal str As String) As Boolean Implements IPlugin.Verarbeite
- Console.WriteLine("Länge der Eingabe: " & str.Length.ToString()) ' Länge des Strings ausgeben
- Return True ' Der Vorgang war erfolgreich, also "True" zurückgeben
- End Function
- End Class
Hier noch ein paar andere Kataloge, die man verwenden kann:
- AggregateCatalog <-- Zum Kombinieren mehrerer Kataloge!
- AssemblyCatalog
- DirectoryCatalog
- TypeCatalog
Hier noch ein Video: youtube.com/watch?v=EFjD1rTcXAQ
Hoffe, Euch hat es geholfen und/oder inspiriert.
nikeee
Von meinem iPhone gesendet
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „nikeee13“ ()