C# 在处理插件时,从组合根目录之外的DI容器解析是否可以?
我正在构建一个WPF(桌面)应用程序,利用依赖注入、DI容器和。我的应用程序在启动期间还从单独的程序集中加载插件,并且这些插件在DI容器中注册。我在启动时在composition root中解析了一次整个对象图,但是,我在解析插件时遇到了一些问题,我想知道是否可以将DI容器注入工厂来解析未知类型? 我的插件全部:C# 在处理插件时,从组合根目录之外的DI容器解析是否可以?,c#,plugins,dependency-injection,simple-injector,di-containers,C#,Plugins,Dependency Injection,Simple Injector,Di Containers,我正在构建一个WPF(桌面)应用程序,利用依赖注入、DI容器和。我的应用程序在启动期间还从单独的程序集中加载插件,并且这些插件在DI容器中注册。我在启动时在composition root中解析了一次整个对象图,但是,我在解析插件时遇到了一些问题,我想知道是否可以将DI容器注入工厂来解析未知类型? 我的插件全部: 实现一个公共接口,IPlugin 需要是暂时的(因为多个实例可以同时存在) 依赖于运行时值在初始化时对其进行配置 可能会,也可能不会,通过构造函数注入其他依赖项 我已经开始实现一个
- 实现一个公共接口,
IPlugin
- 需要是暂时的(因为多个实例可以同时存在)
- 依赖于运行时值在初始化时对其进行配置
- 可能会,也可能不会,通过构造函数注入其他依赖项
PluginFactory
来在需要时初始化插件,因为:“任何需要运行时值来构造特定依赖项的地方,抽象工厂都是解决方案。”
但是,我不能在我的工厂中只使用new()
插件,因为我不知道它们的类型和依赖性
我想到了几种解决办法:
PluginFactory
的构造函数中,在运行时解析插件并将其终止。然而,这与存款准备金率模式背道而驰PluginFactory
的构造函数注入IEnumerable插件。但是,这违反了瞬态要求,因为每个实例只有一个副本[*错误]。我可以通过在我所有的插件上实现ICloneable
并在工厂中克隆它们来解决这个问题,但是,这既很难纠正依赖项,又会使所有插件变得混乱
PluginFactory
并使用Activator.CreateInstance()
创建实例。我甚至可以为插件定义一个公共构造函数签名,并用依赖项初始化它们。然而,这违反了我的要求,即插件具有不同的依赖关系,这也导致了错误实例化我的插件的最佳方式是什么(我倾向于#1)。我是否完全错过了一些选择?或者我可能误判了我的其他选择吗?既然你已经提到了马克·希曼,请看看他的文章和他的文章。长话短说,可以将容器引用/注入到构成根的基础结构组件中
因此,解决方案是定义一个
IPluginFactory
抽象,并将其放在应用程序的核心库中。在您的组合根目录中(注意:将CR视为一个层,而不是一个类),您可以为此抽象创建一个实现,在这里可以注入容器。在Simple Injector中,注入的IEnumerable
实例表现为流。这意味着每次迭代可枚举项时,都会再次向容器请求实例。各国:
注意:简单注入器保留返回实例的生活方式
从注入的IEnumerable
实例。事实上,你不应该这样做
请将插入的IEnumerable
作为
实现应该视为实例的强>流< /强>。简单的
Injector将始终向同一个流注入一个引用(
IEnumerable
本身就是一个单例)并且每次迭代
IEnumerable
,对于每个单独的组件,都会询问容器
根据该组件的生活方式解析实例。
不管[consuming component]注册为
singleton,它包装的验证器每个都有自己的特定
生活方式
容器将根据实例的生活方式返回实例。如果实例注册为transient,则意味着每次迭代集合时都会得到一个新实例。从某种意义上说,由简单注入器注入的可枚举项表现为工厂。这里有一个小测试显示了这种行为:
[TestMethod]
public void EnumerablesBehaveAsStreams()
{
// Arrange
var container = new Container();
container.Collection.Register<ILogger>(typeof(SqlLogger), typeof(FileLogger));
IEnumerable<ILogger> loggers = container.GetAllInstances<ILogger>();
// Act
ILogger sqlLogger1 = loggers.First();
ILogger sqlLogger2 = loggers.First();
// Assert
Assert.AreNotSame(sqlLogger1, sqlLogger2);
}
现在集合中的第一个插件注册为singleton,而其他插件仍然是暂时的。如果您想以相同的生活方式注册他们,您可以执行以下操作:
List pluginTypes=LoadPluginTypes();
pluginTypes.ForEach(type=>container.Register(type,lifety.Singleton));
容器、集合、注册(插件类型);
另一个选项是将注册
实例的集合传递到集合中。注册
方法:
List pluginTypes=LoadPluginTypes();
容器。收集。登记(类型(IPlugin),
从输入插件类型
选择Lifestyle.Singleton.CreateRegistration(
类型(IPlugin)、类型、容器);
在这种情况下,以前的注册之间没有实际的区别,但是当您想做更高级的事情时,注册注册
实例变得很有趣
List<Type> pluginTypes = LoadPluginTypes();
container.Collection.Register<IPlugin>(pluginTypes);
public class PluginFactory : IPluginFactory
{
private readonly IEnumerable<IPlugin> plugins;
public PluginFactory(IEnumerable<IPlugin> plugins)
{
this.plugins = plugins;
}
public IPlugin GetPluginByName(string name)
{
return this.plugins.Single(plugin => plugin.Name == name);
}
}