Using Ninject in a plugin like architecture
Asked Answered
W

9

27

I'm learning DI, and made my first project recently.

In this project I've implement the repository pattern. I have the interfaces and the concrete implementations. I wonder if is possible to build the implementation of my interfaces as "plugins", dlls that my program will load dynamically.

So the program could be improved over time without having to rebuild it, you just place the dll on the "plugins" folder, change settings and voilá!

Is this possible? Can Ninject help with this?

Week answered 1/12, 2008 at 13:59 Comment(0)
R
26

While Sean Chambers' solution works in the case that you control the plugins, it does not work in the case where plugins might be developed by third parties and you don't want them to have to be dependent on writing ninject modules.

This is pretty easy to do with the Conventions Extension for 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);
    }
}

You can then get all loaded plugins with kernel.GetAll<IPlugin>().

The advantages of this method are:

  1. Your plugin dlls don't need to know that they are being loaded with ninject
  2. The concrete plugin instances will be resolved by ninject, so they can have constructors to inject types the plugin host knows how to construct.
Rolandorolandson answered 12/12, 2011 at 23:8 Comment(2)
I do pretty much exactly this in one of my projects on found here. I've found that leaving Ninject in charge of actually loading the assemblies will crash your app if one of your plugin dlls doesn't load. This is not good, so I load the assemblies into the app domain first, wrapped in try/catch for better behavior.Rolandorolandson
How could you do the same thing using the latest version of Ninject, Scan is gone?Dog
S
12

This question applies to the same answer I provided over here: Can NInject load modules/assemblies on demand?

I'm pretty sure this is what you're looking for:

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

If you look at KernelBase with reflector in Ninject.dll you will see that this call will recursively load all modules in the loaded assemblies (Load method takes an IEnumerable)

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

I'm using this for scenarios where I don't want a direct assembly reference to something that will change very frequently and I can swap out the assembly to provide a different model to the application (granted I have the proper tests in place)

Striking answered 14/11, 2009 at 5:27 Comment(4)
I'm wondiering if there is potential that a plugin will be able to mess around with your application.Amann
How does this work? If my assembly includes a variety of classes, will it search through for any classes that extend from NinjectModule, or does my assembly need to only contain NinjectModules?Wellrounded
All this does is look for any derived classes of NinjectModule in your assembly and calls the Load() method on each thus initializing your bindings.Striking
According to the Ninject wiki on GitHub, you can auto-load plugins by naming the assembly Ninject.Extensions.*.dll. However, the amount of documentation on this feature is less than the paragraph I'm in the middle of typing.Biagi
W
11

Extending on @ungood good answer, which is based on v.2, with v.3 of Ninject (currently on RC3) it could be made even easier. You needn't any IPluginGenerator anymore, just write:

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

Please note I'm looking for plugins implementing IPlugin (put your interface here) in the same path of the application.

Weiland answered 15/3, 2012 at 12:42 Comment(1)
How can you autoload modules with your solution? I just switched from Ninject 2.2 to 3.0 and I can't autoload modules.Greensward
A
3

you can easily do it with normal C# reflection, you don't need any extra technology.

There are quite a few examples on the web, e.g. http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

In general in your main application, you need to load the assembly implementing the plugin, e.g.:

ass = Assembly.Load(name);

and then you need to create an instance of your plugin. If you know the name of the class it would look like this:

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

and then you just use it.

Aggregate answered 1/12, 2008 at 14:2 Comment(3)
Don't know why this was voted down. It is technically accurate and a valid answer.Ieshaieso
It might be because he doesn't want to know how to create Plugin but how to use DI and change it's DI without having to do compile... so he suggested Plugin...Nones
I haven't vote you down :P why do you vote me down... anyway funnyNones
O
1

Take a look at Managed Extensibility Framework. http://www.codeplex.com/MEF

Oberhausen answered 5/11, 2009 at 17:0 Comment(0)
I
0

There are multiple ways to go about this and you already have accomplished the main goal to achieve this in having concrete implementations through pre-defined interfaces. Realistically, if your interfaces remain stable, you should be able to build off of your core application.

I am not sure how the implementation would work with Ninject, however. You can do this with the Provider Model or with reflection - although I think reflection is overkill, if you don't absolutely need to do it.

With the provider model approach, you place the file in the /bin folder, or any other folder that you are probing, and adjust the .config file to reflect the presence of the provider. If you have a specific "plugin" folder, you can create a method called at the startup of the application and periodically, otherwise, to scan for new or removed instances and reload the providers.

This would work in ASP.NET, under C# or VB. However, if you are doing some sort of other application, you would need to consider another approach. The provider is really just Microsoft's spin on the Strategy Pattern.

Ieshaieso answered 1/12, 2008 at 14:5 Comment(0)
P
0

I got this as a hit for Activator.CreateInstance + Ninject and just wanted to point out something in this area - hopefully it will inspire someone to come up with a real killer answer to this question on SO.

If you havent yet gone to the trouble of auto-scanning Modules and classes and registering them with Ninject properly, and are still creating your plugin via Activator.CreateInstance, then you can post-CreateInstance inject the dependencies in via

IKernel k = ...
var o = Activator.CreateInstance(...);
k.Inject( o );

Of course, this would only be a temporary solution on the way to something like http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c

Periotic answered 28/1, 2009 at 15:26 Comment(0)
C
0

I think no need to framework. This tutorial is solved your problem http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

Casemaker answered 29/11, 2011 at 2:24 Comment(0)
N
-1

The problem is that you might need to recompile if the object you setup in the load of your module are used inside the program. The reason is that you program might not have the latest version of the assembly of your class. Example, if you create a new concrete class for one of your interface, let say you change the plugin dll. Now, Injector will load it, fine but when it will be returned inside your program (kernel.get(...)) your program might not have the assembly and will throw an error.

Example of what I am talking about:

BaseAuto auto = kernel.Get<BaseAuto>();//Get from the NInjector kernel your object. You get your concrete objet and the object "auto" will be filled up (interface inside him) with the kernel.

//Somewhere else:

public class BaseModule : StandardModule
{
        public override void Load(){
            Bind<BaseAuto>().ToSelf();
            Bind<IEngine>().To<FourCylinder>();//Bind the interface
        }     
 }

If you have create a new FourCylinder called SixCylinder, your real program will not have any reference to your new object. So, once you will load from the PlugIn the BaseModule.cs you might get some trouble with the reference. To be able to do it, you will need to distribute the new dll of this concrete implementation with your plugin that will have the Module that Injector will require to load the Interface to Concrete class. This can be done without problem but you start to have a whole application that reside on loading from Plugin and it might be problematic in some points. Be aware.

BUT, if you do want some PlugIn information you can get some tutorial from CodeProject.

Nones answered 1/12, 2008 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.