![](/img/trans.png)
[英]Register and resolve open generic types with many generic parameters with Autofac
[英]Autofac: register open generic types as instances of non-generic abstract parent types?
我正在使用动态灵活的插件系统维护旧版代码,该系统以非常浅的类树的形式实现,所有具体的插件都扩展了抽象的Plugin
类。 大多数这些插件的是具体的类(如LoadSound
, HighPassFilter
),但它们中的一些具有一个一般类型参数(像CreateCopy<T>
该系统的初始化序列基本上会加载所有程序集,列出所有程序集的类型,选择它们是Plugin
后代,然后对其进行迭代,然后将其插入某种自制IoC容器中。
为了提高系统的可维护性,我想用代码库中其他地方使用的Autofac替换自制IoC容器。
我设法以一种有用的方式注册了所有类型,如下所示:
Autofac.ContainerBuilder builder;
// Find all classes derived from Plugin, and split them
// up based on whether or not they are generic
ILookup<bool, Type> isGeneric = types.Where(IsPlugin)
.ToLookup(t => t.IsGenericTypeDefinition);
// Register the generic types as themselves
foreach (Type t in isGeneric[true])
builder.RegisterGeneric(t)
.AsSelf()
.SingleInstance();
// Register all the non-generic types as themselves and
// as instances of Plugin
builder.RegisterTypes(isGeneric[false].ToArray())
.AsSelf()
.As<Plugin>()
.SingleInstance();
这样,我就可以成功解析容器中以后通过builder
创建的任何插件。 问题在于系统还需要GetAvailablePlugins
方法。 我的第一个直觉是将其实现为
public IReadOnlyCollection<PluginMetaData> GetAvailablePlugins()
{
return Components.Resolve<IEnumerable<Plugin>>()
.Select(plugin => new PluginMetaData(plugin.GetType()))
.ToList<PluginMetaData>();
}
(我知道从IoC容器手动解决是个坏主意-但下一步是解决此问题,而不是我现在遇到的问题。)
但是,您可能已经猜到了,这只会导致非通用插件的集合,而不是通用插件的集合。 我天真地尝试用
builder.RegisterGeneric(t)
.AsSelf()
.As<Plugin>()
.SingleInstance();
但是Autofac不需要任何它:
The service 'Vendor.Common.Plugin' is not an open generic type definition.
当然,这是一个准确的观察。 不是。 而且,我也不希望Plugin
的解析尝试会导致泛型插件的具体实例出现-我只希望有一种好的方法来访问所有注册为Plugin
的类型,而不必在其他地方重复该信息!
有任何想法吗?
在写完问题后并没有花几分钟,直到我弄清楚了自己该怎么做:我将注册逻辑更改为
// Find all classes derived from Plugin
IEnumerable<Type> plugins = types.Where(IsPlugin);
// Register all plugins (generic and non-generic) as children of Plugin
builder.RegisterTypes(plugins.ToArray())
.AsSelf().As<Plugin>().SingleInstance();
// Register generic plugins in a resolvable way
foreach (Type t in modules.Where(t => t.IsGenericTypeDefinition))
builder.RegisterGeneric(t).AsSelf().SingleInstance();
我不太确定这是如何工作的,但似乎它以某种方式注册了通用插件,就好像它们是非通用组件一样。 这些非通用组件无法在系统中解析,但在向Autofac询问实现Plugin
服务的所有组件时会返回它们。
然后除此以外,我还将通用插件注册为通用组件,这使系统可以使用具体的类型参数来解析它们。
更新 :但是,我发现现在列出了所有带有
Components.Resolve<IEnumerable<Plugin>>()
.Select(plugin => new PluginMetaData(plugin.GetType()))
.ToList<PluginMetaData>();
这不是一条可行的路线-一些插件具有在运行验证该插件列表的某些属性的测试时无法实例化的依赖项。 所以我结合了我的回答和DR的回答:
我现在使用获取插件列表
Components.ComponentRegistry
.RegistrationsFor(new TypedService(typeof(BaseModule)))
.Select(reg => new PluginMetaData(reg.Activator.LimitType))
.ToList<PluginMetaData>();
另一种方法是查询container.ComponentRegistry
然后以这种方式查找所有已注册的插件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.