2008-12-01 73 views
27

我正在学习DI,并且最近做了我的第一个项目。在一个像体系结构的插件中使用Ninject

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

因此,程序可以随着时间的推移而不必重建,只需将dll放在“plugins”文件夹中,更改设置和voilá!

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

回答

3

你可以很容易地用普通的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); 

然后您只是使用它。

+1

不知道为什么这被拒绝了。它在技术上是准确的和有效的答案。 – 2008-12-01 14:12:07

+1

这可能是因为他不想知道如何创建插件,但如何使用DI并更改它的DI,而无需编译...所以他建议Plugin ... – 2008-12-01 14:13:27

+0

我还没有投票给您:P为什么你把我投给我......无论如何滑稽 – 2008-12-01 14:15:09

-1

问题是,如果在程序中使用在模块加载中设置的对象,则可能需要重新编译。原因是你的程序可能没有你的课程的最新版本。例如,如果您为其中一个界面创建了一个新的具体类,那么假设您更改了插件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,你真正的程序不会有新对象的任何引用。所以,一旦你从PlugIn加载BaseModule.cs,你可能会在引用时遇到一些麻烦。为了能够做到这一点,你将需要使用你的插件分发这个具体实现的新dll,该插件将具有注入器将要将该接口加载到具体类的模块。这可以没有问题地完成,但你开始有一个完整的应用程序驻留在从插件加载,它可能在某些问题上有问题。意识到。

但是,如果你想要一些插件信息,你可以得到一些tutorial from CodeProject

0

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

但我不确定该实现如何与Ninject配合使用。你可以用Provider Model或反思来做到这一点 - 尽管我认为反思过度,如果你不需要这样做的话。

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

这将在ASP.NET,C#或VB下工作。但是,如果您正在做某种其他应用程序,则需要考虑另一种方法。该提供商实际上只是微软在Strategy Pattern上的自旋。

0

我把它当作Activator.CreateInstance + Ninject的一个命中,只是想指出这方面的一些东西 - 希望它能激发人们对SO这个问题提出一个真正的杀手锏。

如果您还没有走了没有自动扫描模块和类的麻烦,并妥善Ninject注册他们,并仍在通过创建您Activator.CreateInstance插件,那么你可以后期通过的CreateInstance

注入的依赖关系
IKernel k = ... 
var o = Activator.CreateInstance(...); 
k.Inject(o); 

当然,这只能是在途中暂时解决像http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c

12

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

我敢肯定这是你在找什么:

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

如果你看看KernelBase与反射器在Ninject.dll中你会看到这个调用将递归地加载所有加载的程序集中的模块(加载方法需要IEnumerable)

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

我正在使用这个场景,我不希望直接汇编引用的东西,将会非常频繁地更改,我可以换出程序集以向应用程序提供不同的模型(已授予我正确的测试)

+1

如果有一个插件可以混淆你的应用程序的潜力,我是wondiering。 – 2010-07-14 12:48:39

25

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

这是很容易与Conventions Extension为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); 
    } 
} 

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

这种方法的优点是:

  1. 你的插件的dll不需要知道他们正在装载ninject
  2. 具体的插件实例将ninject解决,使他们能够有构造函数注入插件主机知道如何构建的类型。
11

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

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

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

相关问题