c# - 为什么将人工引发的Transient Error异常作为AggregateException处理?

标签 c# exception-handling error-handling azureservicebus

当我尝试手动引发 transient 异常时,它总是作为AggregateException处理。由于它是作为AggregateException处理的,因此在我的重试策略中不会将其作为 transient 错误处理,也不会为预定义的重试次数进行重试。

瞬时错误显示为here
因此,我尝试了CommunicationExceptionServerErrorException,但将其作为AggregateException处理。

当我寻找AggregateException时,它说“代表应用程序执行期间发生的一个或多个错误”。是的,它非常有帮助!!!

这是我的案例的示例代码:

我有一个使用ServiceBusTransientErrorDetectionStrategy的重试策略

public void TestManually()
{
    var retryPolicy = new RetryPolicy<ServiceBusTransientErrorDetectionStrategy>(RetryStrategy.DefaultFixed);

    retryPolicy.Retrying += (obj, eventArgs) =>
        {
           Trace.TraceError("Hey!! I'm Retrying, CurrentRetryCount = {0} , Exception = {1}", eventArgs.CurrentRetryCount, eventArgs.LastException.Message);
        };


    retryPolicy.ExecuteAsync(() =>
        MyTestFunction().ContinueWith(t =>
        {    
            if (t.Exception != null) 
            {
                // A non-transient exception occurred or retry limit has been reached
               Trace.TraceError("This was not a transient exxception... It was: " + t.Exception.GetType().ToString());
            } 
        })); 

}


public Task MyTestFunction()
{  
    Task task = Task.Factory.StartNew(() => RaiseTransientErrorManually());
    return task; 
}


public void RaiseTransientErrorManually()
{
    //throw new CommunicationException(); 
    throw new ServerErrorException();
}

假设我这样调用我的函数:
TestManually();

我很困惑,为什么手动抛出的异常(定义为Transient Error)被视为AggregateException?我在那儿想念什么?

谢谢。

最佳答案

异步代码中的异常是一个棘手的问题,原因有两个。

  • 处理异常(例如,通过catch块)的方式并不总是直观的,而且似乎不一致。
  • 库记录异步方法引发的异常行为的方式并不总是很明显。

  • 我将在下面介绍每个项目。

    重要说明:此答案使用术语“异步方法”来指代返回类型为TaskTask<T>的任何方法。内置支持异步编程的语言有其自己的相关术语,其含义可能有所不同。

    异步方法引发的异常

    异步方法能够在创建Task之前或任务本身的异步执行过程中引发异常。尽管项目在异步代码的异常记录方式上并不总是一致的,但我希望在项目中包含以下注释,以便用户清楚理解。

    注意:仅假设以下引用对于明确声明其状态的库为true。该声明专门用于解决上述第二个问题 Realm 。

    The documentation for asynchronous methods does not distinguish between these two cases, allowing for any of the specified exceptions to be thrown in either manner.


    Task创建之前的异常

    在创建表示异步操作的Task对象之前引发的异常必须由调用代码直接捕获。例如,如果代码以这种方式抛出ArgumentNullException,则调用代码将需要包含ArgumentNullExceptionArgumentException的异常处理程序以处理该异常。

    引发直接异常的示例代码:
    public Task SomeOperationAsync()
    {
        throw new ArgumentException("Directly thrown.");
    }
    

    处理直接引发的异常的示例代码:
    try
    {
        Task myTask = SomeOperationAsync();
    }
    catch (ArgumentException ex)
    {
        // ex was thrown directly by SomeOperationAsync. This cannot occur if
        // SomeOperationAsync is an async function (§10.15 - C# Language Specification
        // Version 5.0).
    }
    

    任务执行期间的异常

    在异步执行任务期间引发的异常被包装在AggregateException对象中,并由Exception属性返回。以这种方式抛出的异常必须通过检查Exception属性的任务继续处理,或者通过在包含Wait处理程序的异常处理块内调用Result或检查AggregateException属性来处理。

    在我创建的库中,我为用户提供了额外的保证,内容如下:

    注意:仅假设以下引用对于明确声明其状态的库为true。

    This library additionally ensures that exceptions thrown by asynchronous operations are not wrapped in multiple layers of AggregateException. In other words, an ArgumentException thrown during the asynchronous execution of a task will result in the Exception property returning an AggregateException, and that exception will not contain any nested instances of AggregateException in the InnerExceptions collection. In most cases, the AggregateException wraps exactly one inner exception, which is the original ArgumentException. This guarantee simplifies the use of the API is languages that support async/await, since those operators automatically unwrap the first layer of AggregateException.



    在任务执行期间都会抛出异常的示例方法:
    public Task SomeOperationAsync()
    {
        return Task.StartNew(
            () =>
            {
                throw new ArgumentException("Directly thrown.");
            });
    }
    
    public async Task SomeOtherOperationAsync()
    {
        throw new ArgumentException("async functions never throw exceptions directly.");
    }
    

    在任务执行期间处理异常的示例代码:
    try
    {
        Task myTask = SomeOperationAsync();
        myTask.Wait();
    }
    catch (AggregateException wrapperEx)
    {
        ArgumentException ex = wrapperEx.InnerException as ArgumentException;
        if (ex == null)
            throw;
    
        // ex was thrown during the asynchronous portion of SomeOperationAsync. This is
        // always the case if SomeOperationAsync is an async function (§10.15 - C#
        // Language Specification Version 5.0).
    }
    

    一致的异常处理

    实现异步调用期间发生的异常的特殊处理的应用程序有多个选项可用于一致处理。最简单的解决方案(如果可用)涉及使用 async / await 。这些运算符会自动解包InnerExceptionsAggregateException集合中的第一个异常实例,从而导致行为似乎在调用代码时就好像异常是被调用的方法直接引发的。第二种方法涉及将原始调用视为另一项任务的延续,以确保将所有异常作为AggregateException呈现给异常处理代码。以下代码显示了此策略在现有异步调用中的应用。请注意,CompletedTask类和Then()扩展方法是单独的Rackspace Threading Library(开源,Apache 2.0)的一部分。
    // original asynchronous method invocation
    Task task1 = SomeOperationAsync();
    
    // method invocation treated as a continuation
    Task task2 = CompletedTask.Default.Then(_ => SomeOperationAsync());
    

    使用继续策略进行一致错误处理的代码可能会受益于Catch()方法的使用,该方法也是Rackspace线程库的一部分。此扩展方法的行为类似于 await ,在调用处理异常的延续函数之前,会自动将InnerExceptionsAggregateException集合中的第一个异常实例展开。

    关于c# - 为什么将人工引发的Transient Error异常作为AggregateException处理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26186351/

    相关文章:

    c# - 如何向用户指示哪个 textBox 为空或 null?

    python - 使用 assert 在 python 中引发单元测试

    asp.net - 如何使用 elmah 记录警告

    scala - 无法加载play.http.errorHandler-未调用自定义处理程序

    python - 使用GitHub Decoder脚本解码有效载荷

    c# - 如何减少文本中的多级大括号

    javascript - 从 C# 背后的代码调用 JS 函数

    c# - C#中如何发现Mutex被获取?

    java - 避免在 Android 上使用 try/catch

    .net - MS 考试 70-536 - 如何从线程中抛出和处理异常?