在像体系结构这样的插件中使用Ninject

use*_*415 27 .net c# plugins ninject

我正在学习DI,最近做了我的第一个项目.

在这个项目中,我实现了存储库模式.我有接口和具体实现.我想知道是否有可能将我的接口的实现构建为"插件",我的程序将动态加载的dll.

所以程序可以随着时间的推移而不需要重建它,你只需将dll放在"plugins"文件夹,更改设置和voilá!

这可能吗?Ninject可以帮助解决这个问题吗?

ung*_*ood 26

虽然Sean Chambers的解决方案适用于您控制插件的情况,但是如果插件可能由第三方开发并且您不希望它们必须依赖于编写ninject模块,则它不起作用.

对于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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用所有加载的插件kernel.GetAll<IPlugin>().

这种方法的优点是:

  1. 你的插件dll不需要知道他们正在加载ninject
  2. 具体的插件实例将由ninject解析,因此他们可以使用构造函数来注入插件主机知道如何构造的类型.

  • 我在[找到这里](https://github.com/ungood/BadaBingBot/blob/fc326bc37169dfdf506c16115eb6b200f8fc1afd/BadaBingBot/Program.cs)的一个项目中做了相当多的事情.我发现,如果你的某个插件dll没有加载,让Ninject负责实际加载程序集会使你的应用程序崩溃.这不好,所以我首先将程序集加载到app域中,用try/catch包装以获得更好的行为. (3认同)

Sea*_*ers 12

这个问题适用于我在此提供的相同答案:NInject可以按需加载模块/组件吗?

我很确定这就是你要找的东西:

var kernel = new StandardKernel();
kernel.Load( Assembly.Load("yourpath_to_assembly.dll");
Run Code Online (Sandbox Code Playgroud)

如果在Ninject.dll中查看带有反射器的KernelBase,您将看到此调用将递归加载已加载程序集中的所有模块(Load方法采用IEnumerable)

public void Load(IEnumerable<Assembly> assemblies)
{
    foreach (Assembly assembly in assemblies)
    {
        this.Load(assembly.GetNinjectModules());
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用这种情况,我不希望直接汇编引用会经常更改的内容,我可以换出程序集为应用程序提供不同的模型(授予我适当的测试)

  • 根据GitHub上的Ninject wiki,您可以通过命名程序集Ninject.Extensions.*.dll来自动加载插件.但是,此功能的文档数量少于我在键入过程中的段落. (2认同)

小智 11

扩展@ungood的良好答案,基于v.2,与Ninject的v.3(目前在RC3上),它可以变得更容易.你不再需要任何IPluginGenerator,只需写:

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

请注意我正在寻找实现IPlugin的插件(将你的界面放在这里)在应用程序的相同路径中.


Grz*_*nio 3

您可以使用普通的 C# 反射轻松完成此操作,不需要任何额外的技术。

网上有很多例子,例如 http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

一般来说,在主应用程序中,您需要加载实现插件的程序集,例如:

ass = Assembly.Load(name);
Run Code Online (Sandbox Code Playgroud)

然后您需要创建插件的实例。如果您知道类的名称,它将如下所示:

ObjType = ass.GetType(typename);
IPlugin plugin = (IPlugin)Activator.CreateInstance(ObjType);
Run Code Online (Sandbox Code Playgroud)

然后你就可以使用它了。