【问题标题】:Using Ninject in a plugin like architecture在像架构这样的插件中使用 Ninject
【发布时间】:2008-12-01 13:59:40
【问题描述】:

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

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

因此程序可以随着时间的推移而改进,而不必重新构建它,您只需将 dll 放在“插件”文件夹中,更改设置,瞧!

这可能吗? Ninject 可以帮忙吗?

【问题讨论】:

    标签: c# .net plugins ninject


    【解决方案1】:

    虽然Sean Chambers' solution 在您控制插件的情况下有效,但在插件可能由第三方开发并且您不希望它们必须依赖于编写 ninject 模块的情况下它不起作用。

    使用 Ninject 的 Conventions Extension 很容易做到这一点:

    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);
        }
    }
    

    然后您可以使用kernel.GetAll&lt;IPlugin&gt;() 获取所有已加载的插件。

    这种方法的优点是:

    1. 您的插件 dll 不需要知道它们是用 ninject 加载的
    2. 具体的插件实例将由 ninject 解析,因此它们可以有构造函数来注入插件宿主知道如何构造的类型。

    【讨论】:

    • 我在found here 上的一个项目中几乎就是这样做的。我发现如果您的插件 dll 之一未加载,让 Ninject 负责实际加载程序集将使您的应用程序崩溃。这不好,所以我先将程序集加载到应用程序域中,并用 try/catch 包裹以获得更好的行为。
    • 你怎么能用最新版本的 Ninject 做同样的事情,Scan 没了?
    【解决方案2】:

    这个问题适用于我在这里提供的相同答案:Can NInject load modules/assemblies on demand?

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

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

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

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

    我将它用于我不希望直接程序集引用会非常频繁地更改的东西的情况,并且我可以换出程序集以向应用程序提供不同的模型(假设我有适当的测试)地点)

    【讨论】:

    • 我想知道插件是否有可能干扰您的应用程序。
    • 这是如何工作的?如果我的程序集包含各种类,它会搜索从 NinjectModule 扩展的任何类,还是我的程序集只需要包含 NinjectModules?
    • 所有这一切都是在你的程序集中查找 NinjectModule 的任何派生类,并在每个类上调用 Load() 方法,从而初始化你的绑定。
    • 根据 GitHub 上的 Ninject wiki,您可以通过命名程序集 Ninject.Extensions.*.dll 来自动加载插件。但是,有关此功能的文档数量少于我正在输入的段落。
    【解决方案3】:

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

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

    请注意,我正在寻找在应用程序的同一路径中实现 IPlugin(将您的接口放在这里)的插件。

    【讨论】:

    • 如何使用您的解决方案自动加载模块?我刚从 Ninject 2.2 切换到 3.0,我无法自动加载模块。
    【解决方案4】:

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

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

    通常在你的主应用程序中,你需要加载实现插件的程序集,例如:

    ass = Assembly.Load(name);
    

    然后您需要创建插件的实例。如果你知道类的名称,它看起来像这样:

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

    然后你就用它。

    【讨论】:

    • 不知道为什么这被否决了。它在技术上是准确且有效的答案。
    • 这可能是因为他不想知道如何创建Plugin,而是想知道如何使用DI并更改它的DI而无需编译...所以他建议使用Plugin...
    • 我还没有投票给你 :P 你为什么投票给我...反正很有趣
    【解决方案5】:

    查看托管可扩展性框架。 http://www.codeplex.com/MEF

    【讨论】:

      【解决方案6】:

      有多种方法可以解决这个问题,您已经完成了主要目标,即通过预定义的接口实现具体实现。实际上,如果您的界面保持稳定,您应该能够构建核心应用程序。

      但是,我不确定 Ninject 的实现如何工作。您可以使用 Provider Model 或反射来执行此操作 - 尽管我认为反射是多余的,如果您不是绝对需要这样做的话。

      使用提供程序模型方法,您将文件放在 /bin 文件夹或您正在探测的任何其他文件夹中,并调整 .config 文件以反映提供程序的存在。如果您有一个特定的“插件”文件夹,您可以创建一个在应用程序启动时调用的方法,并定期调用,否则,扫描新的或删除的实例并重新加载提供程序。

      这适用于 ASP.NET,在 C# 或 VB 下。但是,如果您正在执行某种其他应用程序,则需要考虑另一种方法。提供者实际上只是微软在Strategy Pattern 上的转折。

      【讨论】:

        【解决方案7】:

        我认为这是 Activator.CreateInstance + Ninject 的热门产品,只是想指出这方面的一些事情 - 希望它会激发某人在 SO 上对这个问题提出真正的杀手级答案。

        如果您还没有麻烦地自动扫描模块和类并在 Ninject 中正确注册它们,并且仍在通过 Activator.CreateInstance 创建您的插件,那么您可以在 CreateInstance 后通过 via 注入依赖项

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

        当然,这只是通往http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c 之类的临时解决方案

        【讨论】:

          【解决方案8】:

          我认为不需要框架。本教程解决了你的问题http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

          【讨论】:

            【解决方案9】:

            问题是,如果您在模块加载中设置的对象在程序内部使用,您可能需要重新编译。原因是您的程序可能没有您的类程序集的最新版本。例如,如果您为其中一个接口创建一个新的具体类,假设您更改了插件 dll。现在,Injector 将加载它,很好,但是当它在您的程序 (kernel.get(...)) 中返回时,您的程序可能没有程序集并且会抛出错误。

            我所说的例子:

            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
                    }     
             }
            

            如果您创建了一个名为 SixCylinder 的新 FourCylinder,那么您的真实程序将不会引用您的新对象。因此,一旦您从插件中加载 BaseModule.cs,您可能会在参考方面遇到一些问题。为了能够做到这一点,您需要使用您的插件分发这个具体实现的新 dll,该插件将具有注入器将接口加载到具体类所需的模块。这可以毫无问题地完成,但是您开始拥有一个驻留在从插件加载的整个应用程序,并且在某些方面可能会出现问题。请注意。

            但是,如果您确实想要一些插件信息,您可以获取一些tutorial from CodeProject

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-12-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-06-03
              相关资源
              最近更新 更多