Erstellen eines Plugin-Systems mit VB.NET und MEF ab .NET 3.5

    • VB.NET

    Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von nikeee13.

      Erstellen eines Plugin-Systems mit VB.NET und MEF ab .NET 3.5

      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:

      VB.NET-Quellcode

      1. Module MainModule
      2. Sub Main()
      3. Console.WriteLine("Bitte geben Sie irgendetwas ein:")
      4. Dim eingabe As String = Console.ReadLine()
      5. Console.WriteLine("Sie haben eingegeben: " & eingabe)
      6. Console.WriteLine("Die Eingabe wird nun verarbeitet...")
      7. Console.WriteLine()
      8. 'TODO: Eingabe verarbeiten
      9. End Sub
      10. 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:

      VB.NET-Quellcode

      1. Public Interface IPlugin
      2. Function Verarbeite(ByVal str As String) As Boolean
      3. End Interface

      (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

      1. Imports MefHostApplication
      2. Public Class EingabeLängePlugin
      3. Implements IPlugin
      4. Public Function Verarbeite(ByVal str As String) As Boolean Implements IPlugin.Verarbeite
      5. Console.WriteLine("Länge der Eingabe: " & str.Length.ToString()) ' Länge des Strings ausgeben
      6. Return True ' Der Vorgang war erfolgreich, also "True" zurückgeben
      7. End Function
      8. 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:

      VB.NET-Quellcode

      1. Imports System.ComponentModel.Composition
      2. <Export(GetType(IPlugin))>
      3. Public Class EingabeLängePlugin
      4. ' (Restlicher Code)


      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:

      VB.NET-Quellcode

      1. <ImportMany(GetType(IPlugin))> _
      2. Public Plugins As IPlugin()


      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

      1. Public Sub LadePlugins()
      2. Dim catalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
      3. ' catalog gibt an, dass die Plugins aus dem Verzeichnis „Plugin“ geladen werden sollen
      4. Dim container = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
      5. container.ComposeParts(Me) ' Hier passiert die Magie!
      6. End Sub

      Achtung, das Plugin-Verzeichnis muss existieren, sonst bekommt Ihr einen Fehler!
      Die ganze PluginVerbindung-Klasse sieht nun so aus:

      VB.NET-Quellcode

      1. Class PluginVerbindung
      2. <ImportMany(GetType(IPlugin))> _
      3. Public Plugins As IPlugin()
      4. Public Sub LadePlugins()
      5. Dim catalog As DirectoryCatalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
      6. Dim container As CompositionContainer = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
      7. container.ComposeParts(Me)
      8. End Sub
      9. End Class


      Diese Klasse verwenden wir nun in unserem Sub Main. Beim Programmstart laden wir die Plugins:

      VB.NET-Quellcode

      1. Sub Main()
      2. Dim verbindung As New PluginVerbindung()
      3. verbindung.LadePlugins()
      4. ' Restlicher Code


      Nun können wir nach der Benutzereingabe durch alle Plugins gehen und nacheinander die Verarbeitungsfunktionen aufrufen:

      VB.NET-Quellcode

      1. For Each plugin As IPlugin In verbindung.Plugins
      2. plugin.Verarbeite(eingabe) ' Gibt zurück, ob Vorgang erfolgreich war; der Boolean ist hier unverwendet
      3. Next


      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

      VB.NET-Quellcode

      1. Imports System.ComponentModel.Composition
      2. Imports System.ComponentModel.Composition.Hosting
      3. Module MainModule
      4. Sub Main()
      5. Dim verbindung As New PluginVerbindung()
      6. verbindung.LadePlugins()
      7. Console.WriteLine("Bitte geben Sie irgendetwas ein:")
      8. Dim eingabe As String = Console.ReadLine()
      9. Console.WriteLine("Sie haben eingegeben: " & eingabe)
      10. Console.WriteLine("Die Eingabe wird nun verarbeitet...")
      11. Console.WriteLine()
      12. For Each plugin As IPlugin In verbindung.Plugins
      13. plugin.Verarbeite(eingabe) ' Gibt zurück, ob Vorgang erfolgreich war; der Boolean ist hier unverwendet
      14. Next
      15. Console.ReadKey()
      16. End Sub
      17. End Module
      18. Class PluginVerbindung
      19. <ImportMany(GetType(IPlugin))> _
      20. Public Plugins As IPlugin()
      21. Public Sub LadePlugins()
      22. Dim catalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
      23. Dim container = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
      24. container.ComposeParts(Me)
      25. End Sub
      26. End Class
      27. Public Interface IPlugin
      28. Function Verarbeite(ByVal str As String) As Boolean
      29. End Interface


      Das Beispielplugin:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.ComponentModel.Composition
      2. Imports MefHostApplication
      3. <Export(GetType(IPlugin))>
      4. Public Class EingabeLängePlugin
      5. Implements IPlugin
      6. Public Function Verarbeite(ByVal str As String) As Boolean Implements IPlugin.Verarbeite
      7. Console.WriteLine("Länge der Eingabe: " & str.Length.ToString()) ' Länge des Strings ausgeben
      8. Return True ' Der Vorgang war erfolgreich, also "True" zurückgeben
      9. End Function
      10. End Class


      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
      Von meinem iPhone gesendet

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „nikeee13“ ()

      Gut gemacht und sehr nützlich. Allerdings war ich bisher immer davon ausgegangen, daß MEF erst fest im Framework 4.0 verankert ist. Aus Kompatibilitätsgründen zu XP (Servicepack SP1), habe ich daher MEF ( System.Addin ) verwendet.
      Gibt es Deiner Meinung nach Vorteile von MEF gegenüber MAF?
      Danke. MEF kann man auch in .NET 3.5 verwenden. Man muss sich nur den Sourcecode von Codeplex laden und in das eigene Projekt einbinden (und selber kompilieren; ist aufwändiger).
      Siehe hier:
      Does MEF require .NET 4? (StackOverflow)

      Von MAF habe ich schon einmal gehört (auch von dem Namespace System.AddIn), aber noch nie verwendet. Vielleicht hilft das hier:
      Choosing between MEF and MAF (System.AddIn) (StackOverflow)
      MAF vs. MEF vs. Prism (StackOverflow)
      What is the difference between MEF and MAF framework (MSDN Forums)
      MEF, MAF and all C# Plugins in-between (mrpfister.com)
      Von meinem iPhone gesendet
      Hi :)

      Zuerst mal, super Tutorial.
      Leider versteh ich nichts :D

      Dazu nun ein paar Fragen, wenn jemand Zeit findet wäre es nett wenn derjenige sie beantworten könnte:

      1. <ImportMany(GetType(IPlugin))> _
      Was genau macht diese Zeile? Ich vermute mal, dass sie jede *.dll
      die auf dem PluginInterface basiert importiert. Richtig so?
      Wofür verwendet man überhaupt diese < > über einzelnen Klassen, Subs, etc.?

      2. 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)
      Was genau ist ein DirectoryCatalog? MSDN hat mir nicht geholfen. Selbes mit CompositionContainer.
      Was tut ComposeParts und warum bergibt man "Me"?

      3. Gibt es Möglichkeiten einzelne Plugins anzusprechen?
      Ala "Für diese Operation stehen 2 Plugins zur Verfügung. Bitte wählen Sie eines aus:"
      Hintergrund: Man übergibt z.B. einen http:// Link an die Validate-Funktion aller Plugins.
      Das Plugin prüft, ob es mit dem übergebenen String etwas anfangen kann, in diesem Falle also
      ein -Link. Falls ja gibt es True zurück, falls nein False.
      Wenn jetzt mehrere Plugins etwas mit dem -Link anfangen können, wie kann der
      Nutzer entscheiden welches Plugin diesen verarbeiten soll?
      -> Done

      Grüße
      "Life isn't about winning the race. Life is about finishing the race and how many people we can help finish the race." ~Marc Mero

      Nun bin ich also auch soweit: Keine VB-Fragen per PM! Es gibt hier ein Forum, verdammt!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nikx“ ()

      1. Diese < > sind dafür da, um Attribute für Klassen, Subs, Funktionen, Properties usw. anzugeben
      Beispiel: Ich lege für eine Property fest, dass sie im Designer in der Auflistung der Eigenschaften angezeigt werden kann

      VB.NET-Quellcode

      1. <Browsable(True)> _
      2. Public Property BlaBla As String
      3. End Property


      2. So genau kenne ich mich da jetzt nicht aus aber ich versuche es mal.
      Ein Katalog enthält, wie du weißt, z.B. ein Reisekatalog, viele Angebote für günstige Reisen und so weiter. Ist vielleicht jetzt nicht das beste Beispiel aber es sollte reichen. Ein Katalog ist also eine Sammlung von verschiedenen Dingen einer Art. Ein DirectoryCatalog ist nicht, wie der Name sagt, eine Sammlung von Verzeichnissen, sondern halt eher eine Auflistung von DLLs(Klassenbibliotheken) welche PlugIns für das Programm enthalten.

      Zu CompositionContainer: Composition ist Englisch und heißt auf Deutsch Komposition oder auch Zusammensetzung. In MSDN steht, dass der CompositionContainer die Kompisition von Teilen verwaltet. Wahrscheinlich sieht der CompositionContainer die PlugIns als Teile eines Ganzen. Dazu wird wahrscheinlich auch dein Programm gehören. Dann fügt der CompositionContainer diese ganzen Teile zu einem Ganzen zusammen, damit es zusammen arbeiten kann. Und genau das macht der CompositionContainer mit ComposeParts.

      MfG
      Jonas Jelonek
      Ja, dass mit den <> ist schon Gewöhnungsbedürftigt. Man kann sehr viel darüber einstellen. Es wird zum Beispielt benutzt um darüber das Attribute Author zu generieren. Darüber werden die einen oder anderen auf jeden Fall stolpern...

      Dieses Tutorial, ist wirklich sehr schön gemacht. Aber es erklärt wirklich nur das "Wie mache ich das". Aber eine ausführliche Erklärung fehlt. Microsoft hat eine wirkliche gute Dokumentation zu diesem Thema veröffentlich. Siehe dazu MEF in der MSDN. Wer es Deutsch brauch: msdn.microsoft.com/de-de/library/dd460648.aspx

      Der offensichtlichste Punkt liegt aber in der dynamik, die damit einher gehen kann.

      Ich rede hier mal aus meiner eigenen Erfahrung:

      Ich besitze einen HTPC (HomeTheater Personal Computer) / PVR (Personal Videorecorder). Also das was die aktuellen neuen Fernseher/Flatscreens können, kann mein HTPC schon seit 5 Jahren. Es ist nur ein Windows, mit entsprechender Hardware und Fernbedienung und eben selbst gestalteter Software, welcher das TV im gesamten Haus zur Verfügung stellt. Auf diesem Server läuft aber auch das TV im Wohnzimmer. Ich möchte das alle Video und Audio Codecs dazu immer aktuell sind.

      Also habe ich mir ein Programm geschrieben, welches mir anzeigt, welche Version ich von einem Codec installiert habe und welche die aktuellste Version ist. Ist eine Version aktueller, dann kann ich diese anhaken und diese wird herunter geladen und automatisch installiert, wenn das /Silent Attribut unterstützt wird.

      Für die einzelnen Codecs wollte ich nicht ständig die Hauptanwendung neu schreiben. Ich wollte die Codecs als Plugin haben, damit auch andere ohne große Kenntnisse meines Programms mir ein Codec Plugin schreiben können. Dafür wären noch nicht einmal weitgehende VB Kenntnisse erforderlich, weil fast nur Propertys gefüllt werden.

      Doch so, kann ich meiner Anwendung ein Modulares System aneignen ohne das ich selbst ein PluginSystem entwickeln muss. Jede Name.dll im Codec Verzeichnis wird so extrem einfach in meine Anwendung integriert. Natürlich macht dieses Plugin nichts, es muss schon explizit angesprochen werden.

      Das explizite Ansprechen wird aber durch die Schnittstelle definiert.

      VB.NET-Quellcode

      1. Public Interface IAuto
      2. Name as String
      3. Sitze as Integer
      4. Raeder as Integer
      5. End Interface


      Erzeugst du unten die Klasse Volkswagen und implementierst das obrige Interface, wird automatisch die Struktur so erweitert, das alle Vorgaben des Interfaces erfüllt sind. Indem nämlich der Code automatisch erweitert wird. Damit gewährleistet du, dass die Plugins deine Struktur erfüllen und du nicht an einem Fehler scheiterst, weil eine Funktion zum Beispiel nicht vorhanden ist, obwohl sie wichtig wäre (weil du sie ja in deinem Hauptprogramm aufrufst).

      VB.NET-Quellcode

      1. Public Class Volkswagen 'Muss man selber schreiben
      2. implements IAuto 'Muss man selber schreiben
      3. Public Name as String implements IAuto.Name 'wird automatisch hinzugefügt
      4. Public Sitze as Integer implements IAuto.Sitze 'wird automatisch hinzugefügt
      5. Public Raeder as Integer implements IAuto.Raeder 'wird automatisch hinzugefügt
      6. End Class 'wird automatisch hinzugefügt


      Damit du in deinem Hautprogramm ein Plugin ansprechen kannst, wäre es natürlich völlig bescheuert, wenn man das Plugin beim direkten Namen kennen würde. Denn kann man es direkt beim Namen nennen, dann ist das kein Plugin, sondern ist hartverdrahteter Code. Mit einem Interface, kannst du jede Erweiterung die auf diesem Interfave basiert über deinen eigenen Namen ansprechen. Um ein Interface als solches zu markieren, gibst du ihm den Buchstaben "I" vor dem eigentlichen Namen.

      VB.NET-Quellcode

      1. Public Sub OutputPluginName(plugin as IAuto)
      2. MessageBox.Show(plugin.Name)
      3. End Sub


      Damit wirst du jedes Plugin, welches auf dem Interface IAuto basiert aufrufen können. Weil nämlich Name vorausgesetzt ist.
      Eine Schnittstelle ist eine Definition, die andere dazu zwingt die Variablen und Funtionen und Subs zu implentieren.

      Damit gewährleistet du eine Minimal Funtion deiner Anwendung und ermöglichst es anderen eigene Interpretationen zu schreiben.

      So weit.
      Ich habe da leider momentan ein Problem. Ich nutze C# und habe eine Klasse fuer die Plugin-Verwaltung erstellt. Mein Problem besteht jedoch jetzt darin, dass ich bei einer Klasse bei container.ComposeParts() als Argument nicht this einsetzen kann. Was muss ich da jetzt einsetzen?
      @Jonas Jelonek: Sieh' dir das hier mal an. Du musst anscheinend über die Extension in System.ComponentModel.Composition gehen, dann funktioniert's.

      @nikeee13: Gehe ich richtig in der Annahme, dass ich die Attribute auch auf Klassen und Strukturen anwenden kann? Whooaa - ich bin schon ganz hibbelig ab all den Möglichkeiten, die mir gerade vorschweben. :D
      @Higlav:

      Hier mal die Definition des Export-Attributes:

      VB.NET-Quellcode

      1. <AttributeUsageAttribute(AttributeTargets.Class Or AttributeTargets.Method Or AttributeTargets.Property Or AttributeTargets.Field, AllowMultiple := True, Inherited := False)>
      2. Public Class ExportAttribute
      3. Inherits Attribute


      Das ExportAttribute lässt sich somit nur auf Klassen, Methoden, Eigenschaften und Feldern anwenden. Structs dann wohl nicht - mir fällt aber auch gerade kein Anwendungsfall ein, wo ein Struct sinnvoll wäre.
      Von meinem iPhone gesendet

      nikeee13 schrieb:

      VB.NET-Quellcode

      1. Class PluginVerbindung
      2. <ImportMany(GetType(IPlugin))> _
      3. Public Plugins As IPlugin()
      4. Public Sub LadePlugins()
      5. Dim catalog As DirectoryCatalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
      6. Dim container As CompositionContainer = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
      7. container.ComposeParts(Me)
      8. End Sub
      9. End Class
      Ich hab mal damit rumgespielt und basierend auf einem Basis-Interface 2 abgeleitete Interfaces generiert, eines wie das Beispiel mit zwei Operanden (+, -, *, /) und eines für Operationen mit einem Operanden (Sinus(), Cosinus(), ...).
      Das geniale ist, dass container.ComposeParts(Me) alle beide Listen wohlsortiert aus dem Katalog befüllt. :thumbup:

      VB.NET-Quellcode

      1. Class PluginVerbindung
      2. <ImportMany(GetType(IPlugin1))> _
      3. Public Plugins1 As IPlugin1()
      4. <ImportMany(GetType(IPlugin2))> _
      5. Public Plugins2 As IPlugin2()
      6. Public Sub LadePlugins()
      7. Dim catalog As DirectoryCatalog = New DirectoryCatalog("./Plugin/") 'Eine Import-Quelle angeben (gibt noch andere)
      8. Dim container As CompositionContainer = New CompositionContainer(catalog) 'Quelle zum Container hinzufügen
      9. container.ComposeParts(Me)
      10. End Sub
      11. End Class
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Tolles Tutorial, kann ich auch Windows Forms übergeben?? Weil dann könnte ich mir sehr gut verstelen ein GUI Basierendes Pluginsystem zu bauen was vornehmlich dazu in der lage sein soll den Plugins zu ermöglichen in einen MDI-Container eigene Toolstrips, und Toolstrip Itemas an beliebigen stellen zu eröffnen. !

      zn-gong schrieb:

      Windows Forms übergeben?
      Ja.
      Du musst nur die System.Windows.Forms-DLL der Plugin-DLL als Verweis hinzufügen.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Garnicht, du verweißt auf das PluginInterface

      <Host Project>--<Interface Projct>--<Dein Plugin Project-->

      Ich hätte da mal ne Frage, muss es eigentlich ein Interface sein, weil eine Basis klasse würde sich in manchen fällen doch ehr anbieten?

      LG, Herbrich
      Gut, wollte nur wissen, weil es giebt ja schon einige eigenschaften und Methoden die ich vordefiniert haben möchte, wo es kein sinn machen würde diese zu Inplementieren.

      LG, Herbrich
      In diesem Tutorial wurde MEF 1.0 gezeigt. Wer .NET Core/.NET Standard verwenden will, muss wohl auf MEF 2.0 wechseln.

      Hier gibt es einen Migrationsguide auf MEF 2.0.
      blog.softwarepotential.com/por…0-to-mef-2-0-on-net-core/

      Man benötigt dementsprechend das NuGet-Paket System.Composition.

      Im Falle von .NET Core sollte das Pluginsystem dann auch unter Linux/MacOS laufen.
      Von meinem iPhone gesendet