[英]Register one of many types to an interface using Autofac
Autofac 有大量的注冊技術,其中大部分我都很難理解。 閱讀了一段時間的文檔,我沒有看到任何可以讓我這樣做的東西。
我有一個不受我控制的系統,它根據一些命令行參數實例化某個 class。 這個 class 是所有衍生自公共基礎 class 的眾多產品之一。 例如:
abstract class BaseCommand {}
class CommandOne : BaseCommand {}
class CommandTwo : BaseCommand {}
直到組件注冊之后,我才會知道這個庫將構建哪個派生 class ( CommandOne
或CommandTwo
)。 以下是代碼外觀的粗略輪廓:
static void Main() {
// Do all the autofac registrations
IContainer container = CompositionRoot.Setup();
// Parse command line arguments
ParseCommandLine(type => container.Resolve(type))
}
ParseCommandLine()
方法是構造我之前提到的兩個類中的任何一個的“黑盒”代碼。 它通過調用我傳遞給它的 lambda 來實現。 它永遠不會實例化這兩個類,而只會實例化其中一個。
我需要 Autofac 允許我“延遲注冊” BaseCommand
作為服務。 也就是說,在注冊時,我們不知道會選擇哪個特定的上下文,但我們知道它總是從BaseCommand
派生的。 所以大致是這樣的:
var builder = new ContainerBuilder();
builder.Register(ctx => /*Access the type passed to Resolve() and instantiate that*/).As<BaseCommand>().AsSelf().SingleInstance();
所以基本上我想要的是:
BaseCommand
注冊 1 個具體類型Resolve()
的類型來決定CommandOne
,解析CommandTwo
應該拋出異常)我怎樣才能在 Autofac 中做到這一點?
我不確定你是否能夠 100% 地獲得你想要的,但我認為你可以通過一些間接的方式獲得非常接近的結果。
首先,您需要一種可以在 lambda 表達式中使用的中介物,這樣您就可以延遲實際的注冊內容。 它有幾個工作:
它看起來像這樣:
public static class CommandFilter
{
private static readonly Guid _id = Guid.NewGuid();
public static Type SelectedCommand { get; set; }
public static void Register(ContainerBuilder builder, Type type)
{
// The "secret key" will be required to resolve these,
// so container.Resolve<T>() won't work - it'd have
// to be container.ResolveKeyed<T>(_id) to get it.
builder.RegisterType(type).Keyed(type, _id);
}
public static BaseCommand Resolve(IComponentContext context)
{
// Only resolve the selected implementation, everything else
// stays "hidden."
return context.ResolveKeyed(_id, CommandFilter.SelectedCommand) as BaseCommand;
}
}
現在您需要注冊所有BaseCommand
實現,但使用此過濾器。 這意味着為您進行一些手動裝配掃描。
// WARNING: I can never remember the right direction for
// IsAssignableFrom so check this by testing.
var asm = typeof(BaseCommand).Assembly;
foreach(var type in asm.GetTypes().Where(t => typeof(BaseCommand).IsAssignableFrom(t)))
{
// Register your "secret" keyed registrations.
CommandFilter.Register(builder, type);
}
// Register the BaseCommand itself.
builder.Register(ctx => CommandFilter.Resolve(ctx))
.As<BaseCommand>()
.SingleInstance();
在您的黑匣子中,關鍵是首先設置過濾器類型,然后解析。
ParseCommandLine(type => {
CommandFilter.SelectedCommand = type;
container.Resolve<BaseCommand>();
});
注冊中的 lambda 允許您推遲選擇; 傳遞給ParseCommandLine
的 lambda 可以進行選擇; 只有你想要的那個會得到解決。 其他所有內容都已注冊但實際上無法訪問,並且對container.Resolve<NotTheSelectedCommand>()
的全權委托調用將失敗並出現解析異常,因為它們已注冊為鍵控並且需要鍵來解析。
我實際上並沒有通過編譯器運行它,但似乎這應該可以解決問題。 根據“真實”系統,您可能需要在設置SelectedCommand
的位置加鎖,因為雖然container.Resolve<T>()
是線程安全的,但您的選擇機制在其外部運行。 對於那些在設置SelectedCommand
之前嘗試從命令過濾器中解決的人來說,可能還需要一些錯誤處理,諸如此類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.