在我看來,有幾個因素在起作用。這可能是我對這個問題的誤解,也可能是某個問題的「縮寫」,請耐心等待。
首先我們來談談獲取ITimeSpanFilterSettings
對象的TimeSpanFilter
的分辨率。稍後我們將討論設置對象的參數化,現在讓我們來談談將設置獲取到過濾器。
如果您具有所述的設置,我推斷您有一個對應於每個IFilter
實現的ISomethingFilterSettings
接口。您有TimeSpanFilter
和ITimeSpanFilterSettings
;如果你有一個DateTimeFilter
你會有一個IDateTimeFilterSettings
。
鑑於此,沒有什麼特別的,你需要做的。使用ContainerBuilder
註冊您的各種類型,併發生魔法。
var builder = new ContainerBuilder();
builder.RegisterType<TimeSpanFilter>().As<IFilter>();
builder.RegisterType<TimeSpanFilterSettings>().As<ITimeSpanFilterSettings>();
var container = builder.Build();
// When you resolve, the TimeSpanFilterSettings class gets instantiated
// and injected into the constructor of the TimeSpanFilter.
var filter = container.Resolve<IFilter>();
即使您有多個過濾器,Autofac也會將所有適當的接口連同構造函數參數一起排列。你不必做任何事情。
var builder = new ContainerBuilder();
// Look, Ma! Two filters and settings! :)
builder.RegisterType<TimeSpanFilter>().As<IFilter>();
builder.RegisterType<TimeSpanFilterSettings>().As<ITimeSpanFilterSettings>();
builder.RegisterType<DateTimeFilter>().As<IFilter>();
builder.RegisterType<DateTimeFilterSettings>().As<IDateTimeFilterSettings>();
var container = builder.Build();
// You can resolve collections and get all of the registered filters.
var filterEnumerable = container.Resolve<IEnumerable<IFilter>>();
現在讓我們來談談過濾器設置對象的參數化。這聽起來像你需要得到一些配置,所以讓我們說(爲了方便),配置來自AppSettings
。
隨着Autofac可以寄存器lambda表達式作爲依賴,而不僅僅是一個具體類型,所以你可以做這樣的事情:
var builder = new ContainerBuilder();
builder.RegisterType<TimeSpanFilter>().As<IFilter>();
builder.Register(
ctx =>
{
var config = ConfigurationSettings.AppSettings["my-key"];
return new TimeSpanFilterSettings(config);
}).As<ITimeSpanFilterSettings>();
var container = builder.Build();
// When you resolve, the TimeSpanFilterSettings class gets instantiated
// and injected into the constructor of the TimeSpanFilter.
var filter = container.Resolve<IFilter>();
這種事情是非常方便的,如果你只是有您的設置對象的一個傳入參數。如果有多個參數,你可以使用傳入的上下文參數在lambda做一些解析上的蒼蠅,太:
builder.Register(
ctx =>
{
var config = ConfigurationManager.AppSettings["my-key"];
var other = ctx.Resolve<OtherDependency>();
return new TimeSpanFilterSettings(config, other);
}).As<ITimeSpanFilterSettings>();
但是,如果你有太多的參數,可以讓有些凌亂,所以你還可以使用參數拉姆達這樣您指定將手動注入只有一個參數,其餘的將被自動完成註冊的依賴:
var builder = new ContainerBuilder();
builder.RegisterType<TimeSpanFilterSettings>().WithParameter(
// Parameter selector determines which parameter this
// thing is referring to - here the constructor parameter
// is called "config" and has to be a System.String.
(pinfo, ctx) =>
{
return
pinfo.Name == "config" &&
pinfo.ParameterType == typeof(string);
},
// Value provider gets the value that should be injected
// and returns it.
(pinfo, ctx) =>
{
return ConfigurationManager.AppSettings["my-key"];
});
任何一項都將正常工作,它只是取決於你怎麼想去做吧。
其他複雜性:您在此評論中提到您根據用戶選擇的視圖獲取您的設置信息。您需要更新系統以將該設置密鑰放置在Autofac可以訪問的地方。
鑑於您提到了一個「視圖」,我假設您的意思是ASP.NET MVC或類似的東西。一個放置請求級值的地方是HttpContext.Items
。這可能需要在系統中進行一些重新設計。例如,如果依賴項必須作爲控制器上的構造函數/屬性來實現,那麼在控制器實例化之前,您可能需要一些機制來填充HttpContext
中的值。也許在你的控制器上有一個屬性,也許有一個IHttpModule
位於管道中,並具有URL到設置的映射,也許它是其他內容。這不是我們在這個問題上可以處理的事情(否則我們只是在這裏寫下整個產品,對吧?而且我不是那麼想的......)。
一旦它在中央位置像HttpContext.Items
,你可以將其寫入拉姆達登記:
// Need to be able to resolve HttpContext, so...
builder.RegisterModule<AutofacWebTypesModule>();
// Then resolve HttpContext in your registration:
builder.Register(
ctx =>
{
var httpCtx = ctx.Resolve<HttpContextBase>();
var configKey = httpCtx.Items["settings-config-key"];
var config = ConfigurationManager.AppSettings[configKey];
return new TimeSpanFilterSettings(config);
}).As<ITimeSpanFilterSettings>()
.InstancePerHttpRequest();
做出一項屬性或IHttpModule
將呼叫從自己的控制器內DependencyResolver.Current.GetService<IFilter>()
您設置HttpContext.Items
後的替代值。儘管我不喜歡服務地點,許多人認爲它是一種「反模式」,所以如果可以的話,請儘量避免。
的注意事項有關緩存:這聽起來像您的配置價值實際上是從什麼地方數據庫未來 - 一個更昂貴的通話相比,閱讀AppSettings
。您可以將數據庫調用正確地註冊到註冊中,但是如果解決了其中一些問題,則可能會遇到一些有趣的性能問題。這兩種情況下的lambda表達式都會在每次分辨率發生時執行 - 參數值不會緩存給您,除非您使用生命週期(默認值)以外的其他值註冊對象,否則Autofac不緩存創建的對象。這可能意味着很多意外的數據庫調用。根據需要確定緩存是一個留給讀者的練習。
(注意在最後一個例子我用InstancePerHttpRequest
的範圍 - 這意味着你會得到緩存一個Web請求。)
還有一件事對設計:這是一個意見,但一般來說,我嘗試避免「參數化分辨率」,就像它一樣。也就是說,「我想要一個普通的IFilter
,但它需要完全適應這種特定情況。」這聽起來就是你在這裏所擁有的。在這些情況下,我發現儘管我可能需要使用像IFilter
這樣的通用基本級接口,但我還會嘗試使用特定於我的需求的接口。
public interface ICustomSituationFilter : IFilter
我會再使用這些自定義接口作爲我的依賴,而不是試圖把每件事做是通用的。它允許我更容易地將控制器中的「配置」概念分開(不應該配置傳入的依賴關係)並將其推送到我的註冊中 - 我不需要將事情彈入HttpContext.Items
或任何共享類型位置,因爲唯一知道設置的地方是實際的依賴關係註冊。如果可以的話,您可能需要考慮更改設計以打破「選擇視圖」和「使用哪些配置設置」之間的關係。它會讓你的生活更輕鬆。
有關Autofac的wiki頁面:
在很大程度上取決於其中'settingsKey'的來源。你能再描述一下你的情況嗎? –