[英]How to handle constructor exception when using Autofac WcfIntegration
有沒有辦法處理WCF服務的構造函數拋出的異常,當該構造函數接受依賴時, 它是IoC容器(在本例中為AutoFac)導致異常的依賴實例化 ?
考慮具有以下構造函數的WCF服務:
public InformationService(IInformationRetriever informationRetriever)
{
_informationRetriever = informationRetriever;
}
//... the service later makes use of the injected InformationRetriever
該服務使用AutoFac WcfIntegration和AutofacWebServiceHostFactory
(這恰好是一個RESTful服務)。
依賴關系在服務的global.asax.cs中注冊,即:
builder.RegisterType<InformationRetriever>()
.As<IInformationRetriever>()
現在, InformationRetriever
實現在其構造函數中執行一些檢查,以確保一切就緒,以便能夠完成其工作。 當它在此階段發現問題時,它會拋出異常。
但是,我不希望服務的調用者接收AutoFac異常:
An exception was thrown while invoking the constructor ... on type InformationRetriever
我有效地試圖測試:
鑒於 InformationService正在運行
當我調用GetSomeInformation()方法時
並且無法實例化InformationRetriever
然后我想返回一個友好的錯誤消息
並記錄實際的異常
這是我的設計問題,還是有一個已知的模式來克服或防止這個問題?
我一直在尋找,無法找到有關此類問題的任何信息。
以DI風格編寫的對象通常通過兩個獨立的階段:組合和執行。 組合階段是連接依賴項並執行拋出參數異常等操作的地方。 您通常希望保持此階段沒有有意義的行為,因為這樣可以在系統配置中顯示錯誤。 第二階段是執行,您可以使用第一階段(依賴項)的輸出來完成工作。
分離這兩個階段消除了很多模糊性和復雜性。 舉個例子,你不要試圖在給割草機放氣的同時割草坪; 這導致兩個活動變得更加復雜(並且危險!)
在這種情況下, InformationRetriever
通過在其構造函數中執行有意義的工作來混合組合和執行階段。 這種混合正是導致您試圖避免的問題:一個有意義的業務異常被包裝在一個組合異常中。 還不清楚如何處理異常,因為頂級調用程序是Autofac而不是實際要求InformationRetriever
執行工作的組件。
我建議在調用InformationRetriever
時努力進行驗證; 這將刪除Autofac異常,並允許InformationService
在沒有任何欺騙的情況下處理異常情況。
這種方法的一個潛在缺點是驗證將在每次調用InformationRetriever
時發生,而不是在構造函數中發生一次。 你有兩個選擇:1)讓它每次都發生,絕對確定工作是否有效,或者2)跟蹤你是否已經完成檢查,只有你以前沒有做過。
如果選擇#2,則可以通過使用裝飾器將其包裝在同一接口的驗證版本中來保持InformationRetriever
清潔:
public class ValidatingInformationRetriever : IInformationRetriever
{
private readonly IInformationRetriever _baseRetriever;
private bool _validated;
public ValidatingInformationRetriever(IInformationRetriever baseRetriever)
{
_baseRetriever = baseRetriever;
}
public void Foo()
{
if(!_validated)
{
Validate();
_validated = true;
}
_baseRetriever.Foo();
}
private void Validate()
{
// ...
}
}
您可以使用Autofac的裝飾器支持注冊它,如下所示:
builder
.RegisterType<InformationRetriever>()
.Named<IInformationRetriever>("base");
builder.RegisterDecorator<IInformationRetriever>(
(c, inner) => new ValidatingInformationRetriever(inner),
fromKey: "base");
我不是構造函數的忠實粉絲,因為除了錯誤的參數之外的其他原因拋出異常。 我可能會以不同的方式為我的類型建模。 但這里有一些想法。 起初我想過做這樣的事情:
builder
.Register(c => {
try
{
return new InformationRetriever();
}
catch (Exception)
{
return new FailoverInformationRetreiver();
}})
.As<IInformationRetriever>();
...其中FailoverInformationRetreiver
在成員訪問時拋出異常。 另一個想法可能是:
public InformationService(Lazy<IInformationRetriever> informationRetriever)
{
_informationRetriever = informationRetriever;
}
並try/catch
InformationService
用法。 如果在應用啟動時已知InformationRetriever
的可用性,您可以使用另一個選項:
// inside your container builder:
if (InformationRetreiver.IsAvailable())
builder.RegisterType<InformationRetriever>()
.As<IInformationRetriever>()
// inside InformationService, make the dependency optional
public InformationService(IInformationRetriever informationRetriever = null)
{
_informationRetriever = informationRetriever;
}
這些想法有幫助嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.