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

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

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

所以程序可以随着时间的推移而不需要重建它,你只需将dll放在“plugins”文件夹中,更改设置并vo!

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

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

对于Ninject的约定扩展 ,这很容易做到:

public static IKernel CreateKernel() { var kernel = new StandardKernel(); kernel.Scan(scanner => { scanner.FromAssembliesInPath(@"Path\To\Plugins"); scanner.AutoLoadModules(); scanner.WhereTypeInheritsFrom(); scanner.BindWith>(); }); return kernel; } private class PluginBindingGenerator : IBindingGenerator { private readonly Type pluginInterfaceType = typeof (TPluginInterface); public void Process(Type type, Func scopeCallback, IKernel kernel) { if(!pluginInterfaceType.IsAssignableFrom(type)) return; if (type.IsAbstract || type.IsInterface) return; kernel.Bind(pluginInterfaceType).To(type); } } 

然后,您可以使用kernel.GetAll()获取所有加载的插件。

这种方法的优点是:

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

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

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

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

如果你在Ninject.dll中查看带有reflection器的KernelBase,你会看到这个调用将递归加载加载的程序集中的所有模块(Load方法需要一个IEnumerable)

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

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

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

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

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

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

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

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

 ass = Assembly.Load(name); 

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

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

然后你就可以使用它了。

看一下Managed Extensibility Framework。 http://www.codeplex.com/MEF

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

但是,我不确定实现如何与Ninject一起使用。 您可以使用提供者模型或使用reflection来执行此操作 – 尽管我认为reflection是过度的,如果您不是绝对需要这样做的话。

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

这可以在ASP.NET,C#或VB下使用。 但是,如果您正在进行某种其他应用程序,则需要考虑另一种方法。 提供商实际上只是微软在战略模式上的转变 。

我把它作为Activator.CreateInstance + Ninject的热门话题,只是想指出这个领域的一些东西 – 希望它会激发某人在SO上提出这个问题的真正杀手回答。

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

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

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

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

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

我在说什么的例子:

 BaseAuto auto = kernel.Get();//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().ToSelf(); Bind().To();//Bind the interface } } 

如果您创建了一个名为SixCylinder的新FourCylinder,那么您的真实程序将不会引用您的新对象。 因此,一旦您从PlugIn BaseModule.cs加载,您可能会遇到一些参考问题。 为了能够做到这一点,您需要使用您的插件分发此具体实现的新dll,该插件将具有Injector将加载Interface to Concrete类所需的Module。 这可以毫无问题地完成,但是你开始有一个整个应用程序驻留在从插件加载时,它可能在某些方面存在问题。 意识到。

但是,如果您确实需要一些PlugIn信息,可以从CodeProject获得一些教程 。