【问题标题】:Exception is not caught - AggregateException unhandled未捕获异常 - 未处理的 AggregateException
【发布时间】:2016-12-09 00:25:51
【问题描述】:

有谁知道为什么下面的代码没有捕捉到我的 ConnectionException,我已经花了几个小时...

    public async Task LoadContacts(string filter)
    {
        // We will send find contacts message to all servers supporting addressbook.
        var addressBookServers = _addressBookService.GetAddressBookServerList();
        // Create task array here to be able to run each task in parallel manner.
        var tasksToProcess = new List<Task<SearchContactsResultDto>>(addressBookServers.Count);

        for (int i = 0; i < addressBookServers.Count; i++)
            tasksToProcess.Add(_addressBookService.SearchContactsAsync(addressBookServers[i].Id, filterUpCaseNoDiacritics));

        while (tasksToProcess.Count > 0)
        {
            var processedTask = await Task.WhenAny(tasksToProcess);
            tasksToProcess.Remove(processedTask);
            try
            {
                var serverResponse = await processedTask.ConfigureAwait(false);
                var vmToAdd = serverResponse.SearchedContacts
                .Where(sc => !SearchedContacts.Exists(c => c.BabelName == sc.BabelName))
                .Select(sc => CreateSearchContactViewModel(serverResponse.ServerId, null, sc.Name, sc.BabelName, sc.ContactId));

                SearchedContacts.AddRange(vmToAdd);
            }
            catch (ErrorMessageException eme) { Log.Warning(s => s.Set($"An {nameof(ErrorMessageException)} of type {eme.ErrorMessage.Cause} threw as a response to {nameof(Core.Json.Messages.MsgFindContacts)}. See exception details for further information.", eme)); }
            catch (ConnectionException ce) { Log.Info(s => s.Set($"Connection with server cannot be reached. Message of type {nameof(Core.Json.Messages.MsgFindContacts)} cannot be send", ce)); }
            catch (TimeoutException te) { Log.Info(s => s.Set($"Request on a message of type {nameof(Core.Json.Messages.MsgFindContacts)} timeouted. See exception details for further details.", te)); }
            catch (Exception) {}
        }
        IsLoadingContacts = false;
    }

当 SearchContactsAsync 抛出异常时,该异常不会被 LoadContacts 方法捕获,而是作为未处理的 AggregateException 传播。 我写了一些单元测试,它们都通过了,问题出现在运行应用程序中。 感谢您的帮助。

SearchContactsAsync 实现:

public async Task<SearchContactsResultDto> SearchContactsAsync(int serverId, string filter)
    {
        var msgFindContactsRes = await _communicationService.SendFindContactsAsync(serverId, filter)
            .ConfigureAwait(false);

        return new SearchContactsResultDto()
        {
            ServerId = serverId,
            SearchedContacts = msgFindContactsRes.Contacts,
            PageNumber = msgFindContactsRes.PageNumber,
            PageSize = msgFindContactsRes.PageSize
        };
    }

SendFindContacsAsync 实现:

public Task<MsgFindContactsRes> SendFindContactsAsync(int serverId, string filter)
    {
        var serverSender = serverConnectionProvider.ProvideSender(serverId);

        var msgFindContacts = messageFactory.CreateMsgFindContacts(filter);

        return serverSender.SendAsync<MsgFindContactsRes>(msgFindContacts);
    }

SendAsync:

public async Task<TExpectedResponse> SendAsync<TExpectedResponse>(IMessage message)
        where TExpectedResponse : class, IMessage
    {
        if (message == null)
            throw new ArgumentNullException($"Argument {nameof(message)} cannot be null.");

        var response = await _queue.ProvideAsync(message).ConfigureAwait(false);

        if (response is TExpectedResponse)
            return response as TExpectedResponse;
        else throw new InvalidServerResponseException($"Invalid response to a message of type {message.Header.Type}, expected message type: {typeof(TExpectedResponse).FullName}, received message type: {response.Header.Type}. Server id: {_serverConnection.ServerId}");
    }

使用 TPL 队列和 TCS 提供异步:

public Task<TItemResult> ProvideAsync(TItemData item)
    {
        TaskCompletionSource<TItemResult> tcs = new TaskCompletionSource<TItemResult>();

        // async enqueue an item with its task completion source
        _queue.SendAsync<QueueItem<TItemData, TItemResult>>(new QueueItem<TItemData, TItemResult>(tcs, item));

        return tcs.Task;
    }

最后是使用 TaskCompletionSource 引发异常的队列消费者:

private async Task StartConsumer(Func<TItemData, TItemResult> consumer)
    {
        while (await _queue.OutputAvailableAsync())
        {
            QueueItem<TItemData, TItemResult> res = await _queue.ReceiveAsync();
            try
            {
                var result = consumer(res.Data);
                res.Tcs?.SetResult(result);
            }
            catch (Exception e)
            {
                res?.Tcs.SetException(e);
                throw;
            }
        }
    }

【问题讨论】:

  • 捕获 AggregateException 并将其解包(循环通过 InnerExceptions 属性。然后它会告诉您代码的确切问题,并让您了解代码的哪一部分实际上失败了。
  • 尝试从您的方法中删除ConfigureAwait(false)。这只是一个猜测,而我正在尝试使用像您一样的真实服务来重现这一点。
  • 您是否在所有三个“捕获”中都设置了断点,以查看它是否真的到达那里?我认为您的三个“捕获”(ErrorMessageException、ConnectionException 或 TimeoutException,但不是通用的异常捕获块)之一可能会捕获异常,并且 Log.Info()Log.Warning() 方法本身会抛出异常,没有被捕获,因为在您现有的 try catch 之外没有另一个 try catch 块。
  • @Alisson,好点,我试图在其中添加断点,你是对的.. 异常被捕获,无论如何它仍然传播给调用者。我看不出发生这种情况的原因以及如何立即解决它。我试图删除日志调用,但没有任何改变......
  • @Alisson,删除 ConfigureAwait 没有帮助...

标签: c# exception-handling async-await


【解决方案1】:

SearchContactsAsync 的任务在这里

for (int i = 0; i < addressBookServers.Count; i++)
     tasksToProcess.Add(_addressBookService.SearchContactsAsync(addressBookServers[i].Id, filterUpCaseNoDiacritics));

这些任务在 try 块之外等待:

var processedTask = await Task.WhenAny(tasksToProcess);
tasksToProcess.Remove(processedTask);
try
{
    // Rest of your code

因此,如果其中一项任务引发异常,则不会处理它。移动线条

var processedTask = await Task.WhenAny(tasksToProcess);
tasksToProcess.Remove(processedTask);

在 try 块内。

【讨论】:

  • 我认为这些任务并没有在 try 块之外等待。当他调用WhenAny 时,异常还没有传播,因为WhenAny() 只是返回完成的任务。当他等待由WhenAny() 产生的任务时,异常会传播,他在try 块中执行此操作:var serverResponse = await processedTask.ConfigureAwait(false);
  • 是的,你完全正确。 await await Task.WhenAny(tasksToProcess); 会在此时抛出异常。
【解决方案2】:

您没有捕捉到aggregate exception,这就是您的异常未处理的原因。

//Following code is just an outline and you need to correct according to your requirement.

          try {
                 //your task
              }
          catch (AggregateException ae)
          {
             ae.Handle( x => 
                       { 
                            // Handle an ErrorMessageException
                            if (x is ErrorMessageException) {
                                //do your logging
                                return true; //if you handled it through logging or reporting
                            }
                            // Handle an ConnectionException
                            else if(x is ConnectionException){ 
                              //do your logging for connection
                              return true;  //if you handled it through logging or reporting 
                             }
                           else
                               return false; //This leave the exception as unhandled aggregate exception.

                       });
          }

【讨论】:

  • 抱歉,我已经编辑了我的代码以捕获任何异常,但 AggregateException 仍在传播。
  • @LukášKoten,如果将活动包含在任务中,则活动内引发的任何类型的异常都将作为 AggregateException 传播。您需要解开此聚合异常以查看内部出了什么问题。如果您阅读 msdn 链接,它会详细说明 Handle 方法如何切换不同类型的异常。
  • 我认为等待任务应该在内部解开聚合异常,因此它的行为与任何其他异常一样。
  • 我仍然认为服务正在抛出 ConnectionException,而 AggregateException 正在被 catch 块内完成的日志内容抛出,我看不出通用 @987654325 的原因是什么@catch 无法处理异常,如果它被抛出在 try 块内。
【解决方案3】:

哦,好吧,我现在可以看到问题所在。我发布的代码的最后一行......它再次引发了从未被捕获的异常......真是一个糟糕的错误。无论如何,感谢所有参与者的帮助......

【讨论】:

    猜你喜欢
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    相关资源
    最近更新 更多