[英]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.