我正在使用动态灵活的插件系统维护旧代码,该系统被实现为非常浅的类树,其中所有具体的插件都扩展了抽象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
的类型,而不必重复其他地方的信息!
有什么想法吗?
答案 0 :(得分:0)
写完问题后没多分钟,直到我自己弄清楚怎么做:我将注册逻辑更改为
// 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>();
并非可行的方法–一些插件具有一些依赖项,这些依赖项在运行验证该插件列表的某些属性的测试时无法实例化。因此,我结合了答案和D.R.的答案:
我现在使用来获取插件列表
Components.ComponentRegistry
.RegistrationsFor(new TypedService(typeof(BaseModule)))
.Select(reg => new PluginMetaData(reg.Activator.LimitType))
.ToList<PluginMetaData>();
答案 1 :(得分:0)
另一种方法是查询container.ComponentRegistry
,然后以这种方式找到所有已注册的插件。