gpt4 book ai didi

c# - 在后台线程中全局捕获从 WCF 异步调用引发的异常

转载 作者:行者123 更新时间:2023-11-30 22:13:27 25 4
gpt4 key购买 nike

我有一个与 WCF 服务通信的 WPF 应用程序。我目前正在使用以下 async 从我的 ViewModels(我正在使用 MVVM 模式)调用我的 WCF 服务基于模式:

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

因为我遵循 MVVM 模式,所以我有 ICommand我的 ViewModel 公开的公共(public)属性,因此关联的命令实现不会返回 Task<T>对象,因为它们就像事件处理程序。所以异常处理实际上非常简单,即我能够使用以下模式捕获从我的 WCF 服务抛出的任何异常:

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

到目前为止一切顺利,如果服务器抛出一个异常,该异常由于自定义 WCF 行为而自动转换为 SOAP 错误,则一切都按预期进行。

因为我有一些可以在我的服务中随处抛出的常见异常(例如,每个 WCF 操作都可以抛出 AuthenticationException,这将在客户端转换为 FaultException<AuthenticationFaultDetail> 异常),我决定在我的应用程序的一个常见位置处理一些异常,即通过处理 Application.DispatcherUnhandledException事件。这工作得很好,我可以捕获我所有的 FaultException<AuthenticationFaultDetail>异常无处不在,向用户显示错误消息,并阻止应用程序退出:

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.
}
}

一切都很好,因为 FaultException多亏了 async 被扔进了 UI 线程模式和同步上下文在 await 之前/之后切换关键字。

我的问题是,其他异常可能会在我的 UI 线程之外的另一个线程中抛出,例如在 EndPointNotFoundException 的情况下扔在 await proxy.GetSomethingAsync();行(服务器端 WCF 服务关闭的情况)。

这些异常不会在 Application.DispatcherUnhandledException 中处理事件处理程序,因为它们不会在 UI 线程中抛出。我可以在 AppDomain.UnhandledException 中处理它们事件,但除了做一些日志记录和退出应用程序之外,我无法做任何其他事情(基本上没有“e.Handled”之类的属性)。

所以我的问题是:如果在我的应用程序的某个位置进行异步 WCF 调用,我该如何处理后台线程中抛出的异常?

我现在能想到的最好的是像下面这样的东西:

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);
}
}
}

但这需要我重构大量代码(每次异步调用 Web 服务时)。

任何能让我重构大量代码的想法都会有所帮助。

最佳答案

通常,我不太喜欢集中式/全局异常处理。我个人的偏好是要么在任何地方处理异常,要么编写您自己的代理包装器对象来处理/翻译预期的故障异常。

也就是说,您可以考虑一种方法(尽管它需要修改所有命令)。

首先,将实际逻辑分解为 async Task 方法,如下所示:

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();
}

通常,我建议使用 async Task ExecuteAsync 方法和匹配 async void Execute 来实现 async ICommand,它只会执行 等待 ExecuteAsync();。除了 async void 方法不是 awaiting Task 之外,我上面的示例几乎相同。这很危险,我将在下面解释。

将逻辑保留在 async Task 中会给您带来一个巨大的优势:您可以更轻松地进行单元测试。此外,async Task 方法具有不同的异常处理,您可以(ab)使用它们来解决您的问题。

async Task 方法 - 如果返回的 Task 从未被 awaited - 将引发 TaskScheduler.UnobservedTaskException .请注意,这不会使您的进程崩溃(从 .NET 4.5 开始);您的处理程序必须决定最佳响应。由于您的async void 方法等待您的async Task 返回的Task > 方法,任何异常都将在 UnobservedTaskException 中结束。

所以这会起作用,但它有一个严重的副作用:任何未观察到的 Task 异常将在同一个处理程序中结束(不仅仅是来自您的 ICommands).未观察到的任务异常在 .NET 4.5 中更改为默认忽略的原因是这种情况在 async 代码中不再异常。例如,考虑以下代码,它将尝试从两个不同的 url 下载并获取第一个响应:

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

关于c# - 在后台线程中全局捕获从 WCF 异步调用引发的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19158250/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com