2010-10-15 4 views
7

Ich versuche, DI (mit Autofac) in eine vorhandene Windows Forms-Anwendung einzuführen.DI (Autofac) in einer Plugin-Architektur: Ist ein eigener DI-Container pro Plug-In OK?

Diese Anwendung verfügt über eine grundlegende Plug-in-Architektur, bei der jedes Plugin sein eigenes Formular anzeigt. Beim Start der Anwendung Scans Baugruppen für Typen registriert, die IPlugin implementieren, und aktiviert dann diese mit Activator.CreateInstance:

public interface IPlugin 
{ 
    Form MainForm { get; } 
} 

ich kann nicht Änderung dieses vorgegebenen Rahmens. Das bedeutet, dass jede Plugin-Klasse durch Nicht-DI-Mittel instanziiert wird, und es scheint mir, dass ich deshalb einen separaten DI-Container für jedes Plugin starten muss.

Meine Frage ist, erstellt eine separate ContainerBuilder und Container pro Plugin OK und noch einigermaßen effizient? (Es wird ca. 10 verschiedene Plugins geben.) Oder sollte es nur einen DI-Container für die gesamte Anwendung geben?

Ich habe einige Beispielcode meiner aktuellen Lösung unten zur Verfügung gestellt.


using Autofac; 
using System.Windows.Forms; 

public class Plugin : IPlugin // instantiated by Activator 
{ 
    public Form MainForm { get; private set; } 

    public Plugin() // parameter-less constructor required by plugin framework 
    { 
     var builder = new ContainerBuilder(); 
     builder.RegisterModule(new Configuration()); 
     var container = builder.Build(); 

     MainForm = container.Resolve<MainForm>(); 
     //^preferred to new MainForm(...) because this way, I can take 
     // advantage of having dependencies auto-wired by the container. 
    } 
} 

internal class Configuration : Module 
{ 
    protected override void Load(ContainerBuilder builder) 
    { 
     builder.RegisterType<MainForm>().SingleInstance(); 
     // ... more plugin-specific registrations go here... 
    } 
} 

internal class MainForm : Form { /* ... */ } 

Ich bin auch nicht sicher, ob ein Container in der Plugin-Konstruktor erstellen und dann einfach über sie zu vergessen, aber verlassen sie Auto-Verkabelung im Hintergrund zu tun, OK ist?

Antwort

7

Die Containerverwendung sollte idealerweise der Register Resolve Release pattern (RRR) folgen. Ich weiß, dass Sie gesagt haben, dass Sie die aktuelle Activator.CreateInstance-Verwendung nicht ändern können, aber es kann immer noch hilfreich sein zu verstehen, wie es wirklich sein sollte.

Wenn Sie diese Einschränkung nicht hatten, sollte nur eine einzelne Containerinstanz vorhanden sein, die von der übergeordneten Anwendung selbst gehostet wird. Dies könnte dann verwendet werden, um alle Plugins zu erstellen. Dies würde den Plugins ermöglichen, Abhängigkeiten zu teilen. Dies ist die Route von MEF, die auch Erweiterbarkeit Szenarien behandelt.

Nun, da Sie das nicht tun können, ist das nächstbeste, was Sie tun können, einen Container pro Plugin zu haben, wie Sie vorschlagen. An diesem Punkt wird es meistens zu einem Implementierungsdetail. In jedem Plugin sollten Sie immer noch dem RRR-Muster folgen.

Wäre es ineffizient? Es sei denn, Sie haben viele Plugins und erstellen und zerstören sie die ganze Zeit, ein paar verschiedene Container sollten nicht viel ausmachen. Es ist jedoch besser zu messen, als vorzeitige Optimierungen zu erstellen.

In diesem Szenario können Sie einen Container nur freigeben, indem Sie ihn statisch machen. Dies macht jedoch die Dinge komplizierter als sie sein müssen, also gehen Sie diesen Weg nicht, wenn es absolut notwendig ist.

+0

Danke für die Antwort, @Mark. Wenn ich Sie richtig verstehe, folgt mein Beispielcode bereits dem RRR-Muster ... richtig? (Mit der Ausnahme, dass ich den Container nicht freigebe, da er mindestens so lange am Leben bleiben muss wie die Root-Komponente 'MainForm'. Ich denke, der beste Weg wäre, Plugins' IDisposable' zu ​​machen und zu veröffentlichen Der Container in der 'Dispose'-Methode.) – stakx

+2

Es gibt nichts in Ihrem Code, das darauf hinweist, dass Sie RRR nicht befolgen, aber es ist schwer zu sagen. Beachten Sie in jedem Fall, dass der Lifestyle von SingleInstance nur einen containerspezifischen Singleton definiert. Es ist kein echtes Singleton, daher können Sie das MainForm nicht auf diese Weise teilen. –

0

Ich bin auch nicht sicher, ob ein Container in der Plugin-Konstruktor erstellen und dann einfach über sie zu vergessen, aber verlassen sie Auto-Verkabelung im Hintergrund zu tun, OK ist?

Ein Blick auf meine eigene Frage viele Monate später, ich wage zu sagen, dass zu vergessen, nur die container nicht in Ordnung ist, da (a) es IDisposable ist und sollten als solche behandelt werden, und (b) einiger Lebensdauer der Komponenten sind an die Container gebunden, daher sollte die Lebensdauer des Containers explizit beendet werden; sei es in der Dispose Methode des Formulars oder wenn sein FormClosed Ereignis ausgelöst wird.