捕获异步代码中的异常同步调用

时间:2015-02-24 23:14:17

标签: c# .net exception asynchronous asp.net-membership

我有用于身份验证的thrift服务。除非我在Task.Run中调用它,否则catch (AccountNotFoundException)不会捕获异常。奇怪的是,测试用例很好。为什么?是因为task.start()与catch不同吗?

    public override string GetUserNameByEmail(string email)
    {
         var task = client.GetUserByEmail(email, false);
         return task.Result;
         // I changed to
         // return Task.Run(() => client.GetUserByEmail(email, false)).Result.UserName;
         // and I was able to catch the exception
    }

    public async Task<AccountDetails> GetAccountDetailsByEmail(string email)
    {
        try
        {
            return await Call(() => client.getAccountDetailsByEmail(email));
        }
        catch (AccountNotFoundException)
        {
            return null;
        }
    }

    private async Task<T> Call<T>(Func<T> call)
    {
        try
        {
            transport.Open();
            var thriftTask = new Task<T>(call);
            thriftTask.Start();
            return await thriftTask;
        }
        catch (DatabaseException e)
        {
            Logger.Error(e);
            throw;
        }
        finally
        {
            transport.Close();
        }
    }

测试用例工作得很好

    [TestMethod]
    public async Task Nonexisting_User_I_Expect_To_Be_Null()
    {
        var user = Provider.GetUser("idontexist@bar.com", false);
        Assert.IsNull(user);
    }

编辑:

我有一个理论为什么我的代码运行正常:代码工作正常,因为我很幸运。请求和异步由同一个线程处理,因此它共享相同的上下文,因此它不会阻塞。

2 个答案:

答案 0 :(得分:3)

首先,您不应该同步调用异步方法。正如我在博客中描述的那样approach you're using is prone to deadlocks

您看到意外异常类型的原因是Result将在AggregateException中包装任何任务异常。为避免这种情况,您可以致电GetAwaiter().GetResult()

这与Start没有任何关系,但是既然你提到它,那么Start成员并没有真正的用例。从来没有充分的理由使用它。相反,请使用Task.Run

var thriftTask = Task.Run(call);

答案 1 :(得分:1)

有关异步代码的异常处理的详细信息,请参阅此处。可能是您正在捕获一个AccountNotFoundException,当您真的要捕获一个Exception时,会将 InnerException 设置为AccountNotFoundException:

https://msdn.microsoft.com/en-us/library/0yd65esw.aspx

摘录:

任务的IsFaulted属性设置为True,任务的Exception.InnerException属性设置为异常,异常将在catch块中捕获。 < / p>

    public async Task DoSomethingAsync()
    {
        Task<string> theTask = DelayAsync();

        try
        {
            string result = await theTask;
            Debug.WriteLine("Result: " + result);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Exception Message: " + ex.Message);
        }
        Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
        Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
        if (theTask.Exception != null)
        {
            Debug.WriteLine("Task Exception Message: "
                + theTask.Exception.Message);
            Debug.WriteLine("Task Inner Exception Message: "
                + theTask.Exception.InnerException.Message);
        }
    }

    private async Task<string> DelayAsync()
    {
        await Task.Delay(100);

        // Uncomment each of the following lines to 
        // demonstrate exception handling. 

        //throw new OperationCanceledException("canceled");
        //throw new Exception("Something happened.");
        return "Done";
    }