异步/等待异常和Visual Studio 2013调试输出行为

1 c# web-services visual-studio-debugging async-await

我正在开发一个C#应用程序,它通过Web服务与Dynamics NAV进行通信.为了减少重复代码并且因为会有许多端点,我创建了一个通用的async/await方法来执行服务调用和处理异常.

该方法有效,但是当发生异常(并处理)时,我在Visual Studio 2013输出窗口中看到了意外行为.

测试代码和输出如下所示.

我担心的是"类型......的第一次机会异常",我在使用async/await方法时会看到4次.这个例外真的会发生4次吗?

同步调用服务时,只有一个预期的异常行.

这只是Visual Studio 2013还是我的async/await代码有问题?

有没有更好的方法来做我想要完成的事情?

class Program
{
    static void Main(string[] args)
    {
        Debug.WriteLine("Synchronous...");
        try
        {
            TestFunctions_PortClient service = new TestFunctions_PortClient();

            service.Open();

            string result = service.ErrorTest();

            Debug.WriteLine(result);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }

        Debug.WriteLine(string.Empty);
        Debug.WriteLine("Async...");
        NavServiceTest navService = new NavServiceTest();

        navService.TestAsync();

        Console.ReadLine();
    }
}

class NavServiceTest
{
    public async void TestAsync()
    {
        try
        {
            string result = await CallServiceAsync();

            Debug.WriteLine(result);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

    private async Task<string> CallServiceAsync()
    {
        TestFunctions_PortClient service = new TestFunctions_PortClient();

        service.Open();

        ErrorTest_Result result = await ExecuteServiceAsync<ErrorTest_Result>(
            service.InnerChannel,
            service.Endpoint,
            service.ErrorTestAsync());

        return result.return_value;
    }

    private async Task<T> ExecuteServiceAsync<T>(IClientChannel channel, ServiceEndpoint endpoint, Task<T> source)
    {
        var tcs = new TaskCompletionSource<T>();
        Task<T> task = tcs.Task;

        try
        {
            Debug.WriteLine("ExecuteServiceAsync");

            tcs.TrySetResult(await source);
        }
        catch (EndpointNotFoundException ex)
        {
            Debug.WriteLine("EndpointNotFoundException");
            tcs.TrySetException(ex);
        }
        catch (FaultException ex)
        {
            Debug.WriteLine("FaultException");
            tcs.TrySetException(ex);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Exception");
            tcs.TrySetException(ex);
        }
        finally
        {
            if (channel != null)
            {
                if (channel.State == CommunicationState.Faulted)
                    channel.Abort();
                else
                    channel.Close();
            }
        }

        if (task.IsFaulted)
        {
            throw task.Exception.InnerException;
        }

        return task.Result;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是上面代码的输出.

Synchronous...
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
Error from NAV

Async...
ExecuteServiceAsync
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
FaultException
A first chance exception of type 'System.ServiceModel.FaultException' occurred in ServiceTest.exe
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
Error from NAV
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 10

当异步方法中发生异常时,它不仅像在同步代码中一样向上传播.哎呀,逻辑堆栈可能不再存在了.

相反,异常存储在表示异步操作的任务中.然后,当你await进行异步操作时,will 的GetResult方法TaskAwaiter将重新抛出原始异常.如果你的代码中没有捕获到它,那么它将被编译器生成的代码再次捕获并放入代表操作的任务中,等等.所以如果你有一系列异步方法(通常就是这种情况)并且最深的一个抛出异常,异常传播实际上是GetResult链中每个链接的"投入,捕获,填充任务".

所以,是的,异常被抛出的4倍,为了有效地只能用一次抛出.如果你担心它的效率,我怀疑它不是太糟糕 - 因为逻辑堆栈跟踪只确定一次.我敢说它的效率低于同步版本,但我的一般理念是,如果你看到很多异常,它们会显着影响你的性能,那么要么你过度使用异常,要么你的系统处于非常糟糕的状态无论如何,性能是你最不担心的.