【发布时间】:2008-12-01 13:59:40
【问题描述】:
我正在学习 DI,最近做了我的第一个项目。
在这个项目中,我实现了存储库模式。我有接口和具体的实现。我想知道是否可以将我的接口实现构建为“插件”,即我的程序将动态加载的 dll。
因此程序可以随着时间的推移而改进,而不必重新构建它,您只需将 dll 放在“插件”文件夹中,更改设置,瞧!
这可能吗? Ninject 可以帮忙吗?
【问题讨论】:
我正在学习 DI,最近做了我的第一个项目。
在这个项目中,我实现了存储库模式。我有接口和具体的实现。我想知道是否可以将我的接口实现构建为“插件”,即我的程序将动态加载的 dll。
因此程序可以随着时间的推移而改进,而不必重新构建它,您只需将 dll 放在“插件”文件夹中,更改设置,瞧!
这可能吗? Ninject 可以帮忙吗?
【问题讨论】:
虽然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<IPlugin>() 获取所有已加载的插件。
这种方法的优点是:
【讨论】:
这个问题适用于我在这里提供的相同答案: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());
}
}
我将它用于我不希望直接程序集引用会非常频繁地更改的东西的情况,并且我可以换出程序集以向应用程序提供不同的模型(假设我有适当的测试)地点)
【讨论】:
扩展基于 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(将您的接口放在这里)的插件。
【讨论】:
您可以使用普通的 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);
然后你就用它。
【讨论】:
查看托管可扩展性框架。 http://www.codeplex.com/MEF
【讨论】:
有多种方法可以解决这个问题,您已经完成了主要目标,即通过预定义的接口实现具体实现。实际上,如果您的界面保持稳定,您应该能够构建核心应用程序。
但是,我不确定 Ninject 的实现如何工作。您可以使用 Provider Model 或反射来执行此操作 - 尽管我认为反射是多余的,如果您不是绝对需要这样做的话。
使用提供程序模型方法,您将文件放在 /bin 文件夹或您正在探测的任何其他文件夹中,并调整 .config 文件以反映提供程序的存在。如果您有一个特定的“插件”文件夹,您可以创建一个在应用程序启动时调用的方法,并定期调用,否则,扫描新的或删除的实例并重新加载提供程序。
这适用于 ASP.NET,在 C# 或 VB 下。但是,如果您正在执行某种其他应用程序,则需要考虑另一种方法。提供者实际上只是微软在Strategy Pattern 上的转折。
【讨论】:
我认为这是 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 之类的临时解决方案
【讨论】:
我认为不需要框架。本教程解决了你的问题http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
【讨论】:
问题是,如果您在模块加载中设置的对象在程序内部使用,您可能需要重新编译。原因是您的程序可能没有您的类程序集的最新版本。例如,如果您为其中一个接口创建一个新的具体类,假设您更改了插件 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。
【讨论】: