在全球范围内捕捉异常在后台线程从WCF异步调用抛出 [英] Globally catch exceptions thrown from WCF async calls in a background thread

查看:346
本文介绍了在全球范围内捕捉异常在后台线程从WCF异步调用抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WPF应用程序与WCF服务进行通信。我目前从我的ViewModels打电话给我WCF服务(我使用MVVM模式)使用以下异步基于模式:

I have a WPF application that communicates with a WCF service. I'm currently calling my WCF service from my ViewModels (I'm using the MVVM pattern) using the following async based pattern:

public async override void MyCommandImplementation()
{
    using (var proxy = new MyProxy())
    {
        var something = await proxy.GetSomethingAsync();
        this.MyProperty = something;
    }
}

因为我下面的MVVM模式,我由我的ViewModels暴露的ICommand 公共属性,所以相关的命令实施,将不会返回任务< T> 的对象,因为他们都喜欢事件处理程序。因此,异常处理实际上是pretty简单,即我能够赶上使用下面的模式我的WCF服务抛出的异常:

As I'm following the MVVM pattern, I have ICommand public properties that are exposed by my ViewModels, so associated command implementation won't return Task<T> objects as they are like event handlers. So the exception handling is actually pretty simple, i.e. I am able to catch any exception thrown from my WCF service using the following pattern:

public async override void MyCommandImplementation()
{
    try
    {
        using (var proxy = new MyProxy())
        {
            var something = await proxy.GetSomethingAsync();
        }
    }
    catch (FaultException<MyFaultDetail> ex)
    {
        // Do something here
    }
}

到目前为止好,如果服务器抛出自动转换为SOAP错误多亏了自定义WCF行为预期的异常的一切工作。

So far so good, everything work as expected if the server throws an exception that is automatically converted to a SOAP Fault thanks to a custom WCF behavior.

由于我有随处在我的服务被抛出(例如每个WCF操作可以抛出一个的AuthenticationException 这将通过转换在客户端到一些常见的异常的FaultException&LT; AuthenticationFaultDetail&GT; 除外),我决定来处理一个共同的地方有例外在我的应用程序,通过处理 Application.DispatcherUnhandledException <即/ code>事件。这工作得很好,我能赶上我所有的的FaultException&LT; AuthenticationFaultDetail&GT; 例外随处可见,显示错误信息给用户,并且prevent从退出应用程序:

As I have some common exceptions that can be thrown everywhere in my service (for instance each WCF operation can throw a AuthenticationException that'll by converted on client side to a FaultException<AuthenticationFaultDetail> exception), I've decided to handle some exceptions in a common place in my application, i.e. by handling the Application.DispatcherUnhandledException event. This works just fine, I can catch all my FaultException<AuthenticationFaultDetail> exceptions everywhere, display an error message to the user, and prevent the application from exiting:

private static void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    // Exception handler that handles some common exceptions
    // such as FaultException<AuthenticationFaultDetail>
    if (GlobalExceptionHandler.HandleException(e.Exception))
    {
        // Expected exception, so we're fine
        e.Handled = true;
    }
    else
    {
        // We're not fine. We couldn't handle the exception
        // so we'll exit the application
        // Log...etc.
    }
}

一切正常pretty很好,因为的FaultException UI线程感谢抛出的异步模式,同步上下文切换前/ 之后的await 关键字。

Everything works pretty well because FaultException are thrown in the UI thread thanks to the async pattern and the synchronization context switch before/after the await keyword.

我的问题是,其他的异常可以在另一个线程比我的UI线程在 EndPointNotFoundException 的情况下被抛出,例如在等待代理抛出.GetSomethingAsync(); 行(在服务器端WCF服务关闭的情况下)

My problem is, other exception can be thrown in another thread than my UI thread, for example in case of EndPointNotFoundException thrown at the await proxy.GetSomethingAsync(); line (case of WCF service shutdown on server side).

这些例外不会在 Application.DispatcherUnhandledException 事件处理程序,因为他们没有在UI线程抛出处理。我可以在 AppDomain.UnhandledException处理它们事件,但我不能做任何事情都要比做一些记录和退出应用程序(基本上没有E。处理般的属性)。

Those exceptions won't be handled in the Application.DispatcherUnhandledException event handler because they are not thrown in the UI thread. I can handle them in the AppDomain.UnhandledException event, but I'm not able to do anything else than do some logging and exit the application (there is basically no "e.Handled"-like property).

所以我的问题是:我怎么能处理异步调用WCF的情况下,在后台线程抛出的异常在一个地方我的应用程序的

So my question is: how could I handle exceptions thrown in background threads in case of an async WCF call in one place of my application?

我能想到的,现在最好的是类似以下内容:

The best I can think of right now is something like the following:

public class ExceptionHandler : IDisposable
{
    public void HandleException(Exception ex)
    {
        // Do something clever here
    }
    public void Dispose()
    {
        // Do nothing here, I just want the 'using' syntactic sugar
    }
}

...

public async override void MyCommandImplementation()
{
    using (var handler = new ExceptionHandler())
    {
        try
        {
            using (var proxy = new MyProxy())
            {
                var something = await proxy.GetSomethingAsync();
            }
        }
        catch (FaultException<MyFaultDetail> ex)
        {
            // Do something here
        }
        catch (Exception ex)
        {
            // For other exceptions in any thread
            handler.HandleException(ex);
        }
    }
}

但是,这需要我重构了很多code(每一次我异步调用Web服务)。

But this would require me to refactor a lot of code (each time I asynchronously call a web service).

任何想法,可以让我的的重构一个巨大的code量将是有益的。

Any idea that would allow me to not refactor a huge amount of code would be helpful.

推荐答案

通常情况下,我不是集中/全局异常处理的忠实粉丝。我个人的preference将是要么处处处理异常或者编写自己的代理包装对象,将处理/转换预期的故障异常。

Normally, I'm not a big fan of centralized/global exception handling. My personal preference would be to either handle the exception everywhere or write your own proxy wrapper object that will handle/translate the expected fault exceptions.

这是说,有一种方法可以考虑(虽然这需要修改所有的命令)。

That said, there is an approach you can consider (though it requires modifying all your commands).

首先,因素,实际的逻辑到异步任务方法,因为这样的:

First, factor the actual logic into an async Task method, as such:

public async Task MyCommandAsync()
{
  try
  {
    using (var proxy = new MyProxy())
    {
      var something = await proxy.GetSomethingAsync();
    }
  }
  catch (FaultException<MyFaultDetail> ex)
  {
    // Do something here
  }
}

public async override void MyCommandImplementation()
{
  MyCommandAsync();
}

通常情况下,我建议实施异步的ICommand s的一个异步任务ExecuteAsync 方法和匹配异步执行无效这将只是做等待ExecuteAsync(); 。我上面的例子是,除了异步无效方法几乎相同的的等待 ING在工作。这是很危险的,我会在下面解释。

Normally, I recommend implementing async ICommands with an async Task ExecuteAsync method and matching async void Execute which will just do await ExecuteAsync();. The example I have above is almost the same except the async void method is not awaiting the Task. This is dangerous and I'll explain below.

保持你的逻辑在异步任务给你一巨大的优势:你可以单元测试更容易。此外,异步任务方法有不同的异常处理,你可以(AB)用于解决您的问题。

Keeping your logic in an async Task gives you one tremendous advantage: you can unit test much more easily. Also, async Task methods have different exception handling which you can (ab)use to solve your problem.

这是异步任务法 - 如果返回工作从不等待编辑 - 将提高<一个href=\"http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception.aspx\"相对=nofollow> TaskScheduler.UnobservedTaskException 。请注意,这会的的崩溃的过程(的.NET 4.5);你的处理器必须决定最好的回应。由于您的异步无效方法的的await ING的任务你的异步任务方法返回的任何异常将在 UnobservedTaskException 结束。

An async Task method - if the returned Task is never awaited - will raise TaskScheduler.UnobservedTaskException. Note that this will not crash your process (as of .NET 4.5); your handler must decide the best response. Since your async void method is not awaiting the Task returned by your async Task method, any exceptions will end up in UnobservedTaskException.

这样的工作,但它有一个严重的副作用:的未观测到的工作异常将在相同的处理刚刚结束了(不那些从你的的ICommand S)。这不可观测的任务异常被改变在.NET 4.5的原因在默认情况下被忽略是因为的这种情况已不再是不寻常的的在异步 code。例如,考虑这个code,将尝试从两个不同的网址,下载和采取的第一个反应:

So that will work, but it has one serious side effect: any unobserved Task exception will end up in the same handler (not just ones from your ICommands). The reason that unobserved task exceptions were changed in .NET 4.5 to be ignored by default is because that situation is no longer unusual in async code. For example, consider this code which will attempt to download from two different urls and take the first response:

async Task<string> GetMyStringAsync()
{
  var task1 = httpClient.GetAsync(url1);
  var task2 = httpClient.GetAsync(url2);
  var completedTask = await Task.WhenAny(task1, task2);
  return await completedTask;
}

在此情况下,如果在错误的较慢的URL的结果,则该异常将被发送到 UnobservedTaskException

In this case, if the slower url results in an error, then that exception will be sent to UnobservedTaskException.

这篇关于在全球范围内捕捉异常在后台线程从WCF异步调用抛出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆