27 Stimmen

Verwendung von Ninject in einer Plugin-ähnlichen Architektur

Ich lerne gerade DI und habe kürzlich mein erstes Projekt gemacht.

In diesem Projekt habe ich das Repository-Muster implementiert. Ich habe die Schnittstellen und die konkreten Implementierungen. Ich frage mich, ob es möglich ist, die Implementierung meiner Schnittstellen als "Plugins", dlls zu bauen, die mein Programm dynamisch laden wird.

So kann das Programm im Laufe der Zeit verbessert werden, ohne dass man es neu erstellen muss. Man legt einfach die dll in den "plugins"-Ordner, ändert die Einstellungen und voilá!

Ist dies möglich? Kann Ninject in diesem Fall helfen?

26voto

ungood Punkte 582

Während Sean Chambers' Lösung funktioniert, wenn Sie die Kontrolle über die Plugins haben. Es funktioniert nicht, wenn Plugins von Dritten entwickelt werden und Sie nicht wollen, dass diese vom Schreiben von Ninject-Modulen abhängig sind.

Dies ist ziemlich einfach mit dem Konventionen Erweiterung für Ninject:

public static IKernel CreateKernel()
{
    var kernel = new StandardKernel();

    kernel.Scan(scanner => {
        scanner.FromAssembliesInPath(@"Path\To\Plugins");
        scanner.AutoLoadModules();
        scanner.WhereTypeInheritsFrom<IPlugin>();
        scanner.BindWith<PluginBindingGenerator<IPlugin>>();
    });

    return kernel;
}

private class PluginBindingGenerator<TPluginInterface> : IBindingGenerator
{
    private readonly Type pluginInterfaceType = typeof (TPluginInterface);

    public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
    {
        if(!pluginInterfaceType.IsAssignableFrom(type))
            return;
        if (type.IsAbstract || type.IsInterface)
            return;
        kernel.Bind(pluginInterfaceType).To(type);
    }
}

Sie können dann alle geladenen Plugins mit kernel.GetAll<IPlugin>() .

Die Vorteile dieser Methode sind:

  1. Ihre Plugin-Dlls brauchen nicht zu wissen, dass sie mit ninject geladen werden.
  2. Die konkreten Plugin-Instanzen werden von ninject aufgelöst, so dass sie Konstruktoren haben können, um Typen zu injizieren, von denen der Plugin-Host weiß, wie sie zu konstruieren sind.

12voto

Sean Chambers Punkte 8360

Diese Frage bezieht sich auf die gleiche Antwort, die ich hier gegeben habe: Kann NInject Module/Baugruppen bei Bedarf laden?

Ich bin mir ziemlich sicher, dass Sie genau das suchen:

var kernel = new StandardKernel();
kernel.Load( Assembly.Load("yourpath_to_assembly.dll");

Wenn Sie sich KernelBase mit Reflektor in Ninject.dll ansehen, werden Sie sehen, dass dieser Aufruf rekursiv alle Module in den geladenen Assemblies lädt (Load-Methode nimmt eine IEnumerable)

public void Load(IEnumerable<Assembly> assemblies)
{
    foreach (Assembly assembly in assemblies)
    {
        this.Load(assembly.GetNinjectModules());
    }
}

Ich verwende dies für Szenarien, in denen ich nicht möchte, dass eine direkte Assembly-Referenz auf etwas, das sehr häufig ändern wird und ich kann die Assembly austauschen, um ein anderes Modell für die Anwendung bereitzustellen (vorausgesetzt, ich habe die richtigen Tests im Ort)

11voto

Pablonete Punkte 1464

In Erweiterung der guten Antwort von @ungood, die auf v.2 basiert, könnte es mit v.3 von Ninject (derzeit auf RC3) noch einfacher gemacht werden. Man braucht keinen IPluginGenerator mehr, einfach schreiben:

var kernel = new StandardKernel();
kernel.Bind(scanner => scanner.FromAssembliesInPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
                                   .SelectAllClasses()
                                   .InheritedFrom<IPlugin>()
                                   .BindToAllInterfaces());

Bitte beachten Sie, dass ich auf der Suche nach Plugins bin, die IPlugin (setzen Sie hier Ihre Schnittstelle ein) im gleichen Pfad wie die Anwendung implementieren.

3voto

Grzenio Punkte 34566

Sie können es einfach mit normaler C#-Reflexion tun, Sie brauchen keine zusätzliche Technologie.

Es gibt eine ganze Reihe von Beispielen im Internet, z. B. http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

Im Allgemeinen müssen Sie in Ihrer Hauptanwendung die Baugruppe laden, die das Plugin implementiert, z. B.:

ass = Assembly.Load(name);

und dann müssen Sie eine Instanz Ihres Plugins erstellen. Wenn Sie den Namen der Klasse kennen, würde es so aussehen:

ObjType = ass.GetType(typename);
IPlugin plugin = (IPlugin)Activator.CreateInstance(ObjType);

und dann benutzt man es einfach.

1voto

epitka Punkte 16561

Schauen Sie sich das Managed Extensibility Framework an. http://www.codeplex.com/MEF

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X