Fire-and-Forget-Ansatz für die MEF-Plugin-Architektur

8

Diese Frage könnte mit dem Design oder dem Code zusammenhängen, aber ich stecke fest, also bin ich offen für jede Art von Antwort; ein Zeiger in der richtigen Weise!

Ich habe MEF (Managed Extensibility Framework) verwendet, um ein Stück WPF-Software zu entwickeln, die als eine Art Orchestrator für Plugins fungiert. Die Anwendung leitet einfach Daten zwischen den Plugins um, je nach Wahl des Benutzers, also was das Plugin tut, ist überhaupt nicht bekannt (besonders, da sie von Drittentwicklern entwickelt werden können). Die Anwendung und das Plugin teilen sich eine Schnittstelle, um zu wissen, welche Methoden aufgerufen werden müssen. Der Datenverkehr läuft also in beide Richtungen: Ein Plugin ruft eine Methode in der Hauptanwendung auf, die Daten sendet und die Hauptanwendung leitet diese Daten an ein anderes Plugin weiter .

Das funktioniert soweit, aber ich habe ein Problem mit synchronem Verhalten. Alle Methoden, die von der Schnittstelle definiert werden, haben keinen Rückgabewert (Void) und ich kämpfe um eine "fire and forget" Art von Ansatz, bei dem die aufrufende Anwendung NICHT rumstehen muss und darauf wartet, dass die Plugins die Codeausführung beenden. und ruft das zurück zur Haupt-App!).

Was ist der beste Ansatz, um das zu lösen? Lassen Sie jedes Plugin (und die Hauptanwendung) seine Arbeitslast auf einen "Stapel" irgendeiner Art legen, nur um das Steuerelement an die aufrufende Seite zurückgeben zu können und dann einen Mechanismus zu haben, der separat durch den Stapel Artikel für Artikel arbeitet (und Verwenden Sie diesen Stacking-Ansatz als Async?)?

Es ist noch erwähnenswert, dass die Plugins in separaten Threads laufen (entsprechend dem Debugger-Thread-Fenster) und wenn sie initialisiert werden, erhalten sie einen Verweis von der aufrufenden Hauptanwendung, damit sie Funktionen in der Haupt-App auslösen können. Die Plugins müssen auch sehr oft der Haupt-App mitteilen, in welchem ​​Status sie sich befinden (Leerlauf, Arbeit, Fehler usw.) und auch Daten senden, die von der Haupt-App protokolliert werden sollen. Dies erzeugt sehr oft eine geschachtelte Aufrufhierarchie (wenn Sie mir folgen) , schwer zu erklären).

Ich verwende .Net 4.5 für diesen.

Im Folgenden finden Sie ein vereinfachtes Beispiel für den Code. Ich habe einige Namen ersetzt, wenn es irgendwo einen Schreibfehler gibt, ist es nur hier und nicht im richtigen Code. :)

Die Schnittstelle:

%Vor%

Das Plugin:

%Vor%

Und die Hauptanwendung:

%Vor%

Es ist die DataReceiver-Funktion in der Haupt-App, die die Daten empfängt, nach dem Plugin sucht und sie dann sendet (über die PlugginTrigger-Funktion).

    
RobertN 07.11.2013, 10:19
quelle

1 Antwort

4

Ein paar Beobachtungen:

  • Fire and Forget ist eine Voraussetzung für den Host , also sollten sich die Plug-in-Implementierungen nicht Gedanken machen.
  • Ich glaube nicht (bitte korrigiere mich, wenn ich falsch liege), die CLR unterstützt das Aufrufen von Methoden auf eine "Feuer-und-vergessen" -weise Weise innerhalb derselben AppDomain. Wenn Ihre Plug-Ins in separate Prozesse geladen wurden und Sie über WCF mit ihnen kommunizieren, können Sie einfach IsOneWay Eigenschaft auf Ihre OperationContractAttribute .

Der zweite Punkt deutet auf eine Lösung hin, die für Ihre Situation leicht übertrieben erscheint - aber lassen Sie es uns trotzdem erwähnen. Ihre Plug-ins können prozessinterne WCF-Dienste hosten, und die gesamte Kommunikation zwischen der WPF-Anwendung und den Plug-ins kann über die WCF-Dienstproxys erfolgen. Dies führt jedoch zu einem Konfigurations-Albtraum und öffnet wirklich eine Dose Würmer für eine ganze Reihe anderer Probleme, die Sie lösen müssten.

Beginnen wir mit einem einfachen Beispiel des Anfangsproblems und versuchen, es von dort zu lösen. Hier ist der Code für die Konsolenanwendung mit einem Plug-In:

%Vor%

Das Problem ist, dass der Aufruf von plugin.Method() blockiert. Um dies zu beheben, ändern wir die Schnittstelle, die der Konsole-Anwendung ausgesetzt ist, wie folgt:

%Vor%

Ein Aufruf einer Implementierung dieser Schnittstelle wird nicht blockiert. Die einzige Sache, die wir ändern müssen, ist die Klasse CompositionHost :

%Vor%

Offensichtlich ist dies ein sehr einfaches Beispiel, und die Implementierung muss möglicherweise leicht variieren, wenn Sie es auf Ihr WPF-Szenario anwenden - aber das allgemeine Konzept sollte trotzdem funktionieren.

    
Lawrence 08.11.2013, 14:07
quelle