調試System.AggregateException-即使在異步代碼中也是如此


顧名思義,AggregateException用於在單個異常中對一個或多個異常進行批處理。在本文中,我將向您展示為什么會發生此異常,以及如何在C代碼中調試它。

錯誤的產生和處理

讓我們從強制一個產生新的AggregateException開始。這個異常在.NET的任務庫中被大量使用,為什么選擇一個包含任務的示例是一件輕而易舉的事情:

Task task1 = Task.Factory.StartNew(() => { throw new ArgumentException(); } );
Task task2 = Task.Factory.StartNew(() => { throw new UnauthorizedAccessException(); } );

try
{
    Task.WaitAll(task1, task2);
}
catch (AggregateException ae)
{
}

在上面的例子中,我們執行兩個任務,每個任務都拋出一個異常。通過調用WaitAll,我們告訴.NET調用並等待這兩個任務的結果。這兩個任務的組合結果將是AggregateException。

調試這個錯誤

從調試器中的上一個示例檢查異常時,我們在InnerExceptions屬性中看到兩個拋出的異常:

 

正如名稱中承諾的那樣,AggregateException是其他異常的包裝器。在該示例中,ArgumentException和UnauthorizedAccessException都可用作內部異常。 在某些情況下,您將為每個異常添加一個catch blog,如下所示:
try
{
    ...
}
catch (ArgumentException ex1)
{
    // Log and re-throw
}
catch (UnauthorizedAccessException ex2)
{
    // Log and swallow
}

這樣,您就可以向用戶生成個別的錯誤消息,記錄異常,但只能重新拋出其中一條或第三條。AggregateException提供了一個方便的名為Handle的小助手方法,而不是在InnerExceptions屬性中的每個異常上循環:

try
{
    ...
}
catch (AggregateException ae)
{
    ae.Handle(inner =>
    {
        // Log inner
        ...
        return inner is UnauthorizedAccessException;
    });
}

在本例中,Handle方法記錄每個異常。func需要返回一個bool,指示是否處理了每個異常。在本例中,我們告訴Handle方法處理了UnauthorizedAccessException,但沒有處理ArgumentException。這將導致AggregateException被拋出回調用代碼,但InnerExceptions屬性中沒有UnauthorizedAccessException(因為我們將該異常標記為已處理)。

來自HttpClient的AggregateExceptions

在處理System.Net.Http.HttpClient類時,可能會遇到AggregateException。這主要是在使用舊API實現異步代碼時(Wait、ContinueWith等)。讓我們看一個例子:
try
{
    var client = new HttpClient();
    var task = client.GetStringAsync("https://httpstat.us/500");
    task.Wait();
    var result = task.Result;
}
catch (AggregateException ex)
{
    throw ex;
}

對https://httpstat.us/500的請求返回一個HTTP狀態代碼500,該代碼拋出一個HttpRequestException。我使用GetStringAsync方法作為示例,但是如果使用其他異步消息(如PostAsync),代碼看起來會很相似。如前所述,taskapi的異常被包裝在AggregateException中,在InnerExceptions屬性中包含HTTP異常。

要獲得實際的異常,您有一系列選項。我將重復使用記錄異常的必要性來說明。讓我們從我在上一個示例中向您展示的Handle方法開始:

try
{
    ...
}
catch (AggregateException ex)
{
    ex.Handle(inner =>
    {
        if (inner is HttpRequestException)
        {
            // Log the exception
            
            return true;
        }
        
        return false;
    });
}

我正在檢查內部異常是否是HttpRequestException類型,在這種情況下,告訴Handle方法這個異常已經被處理。在任何其他場景中,我告訴Handle重新拋出原始異常(通過返回false)。
另一種方法是使用ContinueWith方法捕獲和處理AggregateException:

var client = new HttpClient();
var task = client
    .GetStringAsync("https://httpstat.us/500")
    .ContinueWith(t =>
    {
        try
        {
            return t.Result;
        }
        catch (AggregateException ex)
        {
            ex.Handle(inner =>
            {
                if (inner is HttpRequestException)
                {
                    // Log the exception

                    return true;
                }

                return false;
            });
        }
        catch (Exception ex)
        {
            throw ex;
        }

        return null;
    });
task.Wait();
var result = task.Result;
我基本上只是把try-catch從ContinueWith方法中移了出來。

最后,如果您在.NET4.5上(可能是,如果不是,應該是),則可以使用await關鍵字:

var client = new HttpClient();
try
{
    var result = await client.GetStringAsync("https://httpstat.us/500");
}
catch (HttpRequestException ex)
{
    // Log the exception
}
catch (Exception ex)
{
    throw ex;
}

注意代碼是如何捕獲HttpRequestException而不是AggregateException的。這是因為.NET會自動打開AggregateException並引發底層異常。在大多數情況下,這就是你想要的。移植現有異步代碼以使用await關鍵字的另一個好處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM