簡體   English   中英

如何在使用Autofac WcfIntegration時處理構造函數異常

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM