Autofac:将开放的通用类型注册为非通用抽象父类型的实例吗?

时间:2019-02-22 10:32:06

标签: c# generics inversion-of-control autofac

我正在使用动态灵活的插件系统维护旧代码,该系统被实现为非常浅的类树,其中所有具体的插件都扩展了抽象Plugin类。这些插件大多数都是具体的类(例如LoadSoundHighPassFilter),但是其中一些插件只有一个通用类型参数(例如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的类型,而不必重复其他地方的信息!

有什么想法吗?

2 个答案:

答案 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,然后以这种方式找到所有已注册的插件。

相关问题