C# 在类似架构的插件中使用 Ninject
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/330927/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Ninject in a plugin like architecture
提问by user32415
I'm learning DI, and made my first project recently.
我正在学习 DI,最近做了我的第一个项目。
In this project I've implement the repository pattern. I have the interfaces and the concrete implementations. I wonder if is possible to build the implementation of my interfaces as "plugins", dlls that my program will load dynamically.
在这个项目中,我实现了存储库模式。我有接口和具体的实现。我想知道是否可以将我的接口的实现构建为“插件”,我的程序将动态加载的 dll。
So the program could be improved over time without having to rebuild it, you just place the dll on the "plugins" folder, change settings and voilá!
因此,该程序可以随着时间的推移而改进,而无需重建它,您只需将 dll 放在“插件”文件夹中,更改设置即可!
Is this possible? Can Ninject help with this?
这可能吗?Ninject 可以帮助解决这个问题吗?
回答by Grzenio
you can easily do it with normal C# reflection, you don't need any extra technology.
你可以用普通的 C# 反射轻松地做到这一点,你不需要任何额外的技术。
There are quite a few examples on the web, e.g. http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
网上有不少例子,例如 http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
In general in your main application, you need to load the assembly implementing the plugin, e.g.:
通常在您的主应用程序中,您需要加载实现插件的程序集,例如:
ass = Assembly.Load(name);
and then you need to create an instance of your plugin. If you know the name of the class it would look like this:
然后你需要创建你的插件的一个实例。如果您知道类的名称,它将如下所示:
ObjType = ass.GetType(typename);
IPlugin plugin = (IPlugin)Activator.CreateInstance(ObjType);
and then you just use it.
然后你就使用它。
回答by Patrick Desjardins
The problem is that you might need to recompile if the object you setup in the load of your module are used inside the program. The reason is that you program might not have the latest version of the assembly of your class. Example, if you create a new concrete class for one of your interface, let say you change the plugin dll. Now, Injector will load it, fine but when it will be returned inside your program (kernel.get(...)) your program might not have the assembly and will throw an error.
问题是,如果您在加载模块时设置的对象在程序中使用,您可能需要重新编译。原因是您的程序可能没有最新版本的类程序集。例如,如果您为您的接口之一创建了一个新的具体类,假设您更改了插件 dll。现在,Injector 将加载它,很好,但是当它在您的程序(kernel.get(...))中返回时,您的程序可能没有程序集并会抛出错误。
Example of what I am talking about:
我正在谈论的示例:
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
}
}
If you have create a new FourCylinder called SixCylinder, your real program will not have any reference to your new object. So, once you will load from the PlugIn the BaseModule.cs you might get some trouble with the reference. To be able to do it, you will need to distribute the new dll of this concrete implementation with your plugin that will have the Module that Injector will require to load the Interface to Concrete class. This can be done without problem but you start to have a whole application that reside on loading from Plugin and it might be problematic in some points. Be aware.
如果您创建了一个名为 SixCylinder 的新 FourCylinder,您的实际程序将不会对您的新对象有任何引用。因此,一旦您从 PlugIn 加载 BaseModule.cs,您可能会遇到参考问题。为了能够做到这一点,您需要将这个具体实现的新 dll 与您的插件一起分发,该插件将具有 Injector 加载接口到具体类所需的模块。这可以毫无问题地完成,但您开始拥有一个完整的应用程序,它驻留在从插件加载时,在某些方面可能会出现问题。意识到。
BUT, if you do want some PlugIn information you can get some tutorial from CodeProject.
但是,如果您确实需要一些插件信息,您可以从 CodeProject获得一些教程。
回答by Joseph Ferris
There are multiple ways to go about this and you already have accomplished the main goal to achieve this in having concrete implementations through pre-defined interfaces. Realistically, if your interfaces remain stable, you should be able to build off of your core application.
有多种方法可以解决这个问题,您已经通过预定义的接口实现了具体的实现这一主要目标。实际上,如果您的接口保持稳定,您应该能够构建您的核心应用程序。
I am not sure how the implementation would work with Ninject, however. You can do this with the Provider Modelor with reflection - although I think reflection is overkill, if you don't absolutely need to do it.
但是,我不确定 Ninject 的实现将如何工作。您可以使用提供者模型或反射来做到这一点- 尽管我认为反射是矫枉过正,如果您不是绝对需要这样做。
With the provider model approach, you place the file in the /bin folder, or any other folder that you are probing, and adjust the .config file to reflect the presence of the provider. If you have a specific "plugin" folder, you can create a method called at the startup of the application and periodically, otherwise, to scan for new or removed instances and reload the providers.
使用提供程序模型方法,您将文件放在 /bin 文件夹或您正在探测的任何其他文件夹中,并调整 .config 文件以反映提供程序的存在。如果您有一个特定的“插件”文件夹,您可以创建一个在应用程序启动时调用的方法,否则定期扫描新的或删除的实例并重新加载提供程序。
This would work in ASP.NET, under C# or VB. However, if you are doing some sort of other application, you would need to consider another approach. The provider is really just Microsoft's spin on the Strategy Pattern.
这将适用于 ASP.NET,在 C# 或 VB 下。但是,如果您正在执行某种其他应用程序,则需要考虑另一种方法。提供者实际上只是微软在Strategy Pattern上的旋转。
回答by Ruben Bartelink
I got this as a hit for Activator.CreateInstance + Ninject and just wanted to point out something in this area - hopefully it will inspire someone to come up with a real killer answer to this question on SO.
我认为这是对 Activator.CreateInstance + Ninject 的一个打击,只是想指出这方面的一些东西 - 希望它会激发人们对 SO 上的这个问题提出真正的杀手级答案。
If you havent yet gone to the trouble of auto-scanning Modules and classes and registering them with Ninject properly, and are still creating your plugin via Activator.CreateInstance, then you can post-CreateInstance inject the dependencies in via
如果您还没有遇到自动扫描模块和类并在 Ninject 中正确注册它们的麻烦,并且仍在通过 Activator.CreateInstance 创建您的插件,那么您可以通过 post-CreateInstance 注入依赖项
IKernel k = ...
var o = Activator.CreateInstance(...);
k.Inject( o );
Of course, this would only be a temporary solution on the way to something like http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c
当然,这只是一个临时解决方案,比如http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c
回答by epitka
Take a look at Managed Extensibility Framework. http://www.codeplex.com/MEF
查看托管可扩展性框架。http://www.codeplex.com/MEF
回答by Sean Chambers
This question applies to the same answer I provided over here: Can NInject load modules/assemblies on demand?
这个问题适用于我在此处提供的相同答案:Can NInject load modules/assemblies on demand?
I'm pretty sure this is what you're looking for:
我很确定这就是你要找的:
var kernel = new StandardKernel();
kernel.Load( Assembly.Load("yourpath_to_assembly.dll");
If you look at KernelBase with reflector in Ninject.dll you will see that this call will recursively load all modules in the loaded assemblies (Load method takes an IEnumerable)
如果您查看 KernelBase 和 Ninject.dll 中的反射器,您将看到此调用将递归加载已加载程序集中的所有模块(Load 方法采用 IEnumerable)
public void Load(IEnumerable<Assembly> assemblies)
{
foreach (Assembly assembly in assemblies)
{
this.Load(assembly.GetNinjectModules());
}
}
I'm using this for scenarios where I don't want a direct assembly reference to something that will change very frequently and I can swap out the assembly to provide a different model to the application (granted I have the proper tests in place)
我将它用于以下场景:我不希望直接程序集引用会频繁更改的内容,并且我可以换出程序集以向应用程序提供不同的模型(假设我有适当的测试)
回答by Hùng Lê Xuan
I think no need to framework. This tutorial is solved your problem http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
我认为不需要框架。本教程解决了您的问题http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
回答by ungood
While Sean Chambers' solutionworks in the case that you control the plugins, it does not work in the case where plugins might be developed by third parties and you don't want them to have to be dependent on writing ninject modules.
虽然Sean Chambers 的解决方案在您控制插件的情况下有效,但在插件可能由第三方开发并且您不希望它们必须依赖编写 ninject 模块的情况下,它不起作用。
This is pretty easy to do with the Conventions Extensionfor 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);
}
}
You can then get all loaded plugins with kernel.GetAll<IPlugin>()
.
然后,您可以使用kernel.GetAll<IPlugin>()
.
The advantages of this method are:
这种方法的优点是:
- Your plugin dlls don't need to know that they are being loaded with ninject
- The concrete plugin instances will be resolved by ninject, so they can have constructors to inject types the plugin host knows how to construct.
- 您的插件 dll 不需要知道它们正在加载 ninject
- 具体的插件实例将由 ninject 解析,因此它们可以有构造函数来注入插件主机知道如何构造的类型。
回答by Pablonete
Extending on @ungood good answer, which is based on v.2, with v.3 of Ninject (currently on RC3) it could be made even easier. You needn't any IPluginGenerator anymore, just write:
扩展基于 v.2 的 @ungood good answer,使用 Ninject 的 v.3(目前在 RC3 上)它可以变得更容易。您不再需要任何 IPluginGenerator,只需编写:
var kernel = new StandardKernel();
kernel.Bind(scanner => scanner.FromAssembliesInPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
.SelectAllClasses()
.InheritedFrom<IPlugin>()
.BindToAllInterfaces());
Please note I'm looking for plugins implementing IPlugin (put your interface here) in the same path of the application.
请注意,我正在寻找在应用程序的同一路径中实现 IPlugin(将您的界面放在这里)的插件。