在Silverlight中捕获WCF异常的最佳方法是什么?

14
我有一个使用 WCF 服务的 Silverlight 2 应用程序,因此它对服务方法的所有调用都使用异步回调。如果在这些调用之前或期间服务未运行、崩溃或网络中断等,就会生成异常,正如您所预期的那样。问题是,我不知道如何捕获这个异常。
- 由于它是异步调用,我无法使用 try/catch 包装我的开始调用,并在程序从该点移开后捕获发生的异常。 - 由于服务代理是自动生成的,我无法在每个调用 EndInvoke 的生成函数上放置 try/catch 块(异常实际上出现在此处)。这些生成函数也被调用堆栈中的外部代码包围,因此没有其他地方可以在堆栈中放置 try/catch。 - 我无法将 try/catch 放在我的回调函数中,因为异常发生在它们被调用之前。 - 在我的 App.xaml.cs 中有一个 Application_UnhandledException 函数,它捕获所有未处理的异常。我可以使用它,但这似乎是一种混乱的方式。我宁愿将此功能保留给真正意外的错误(即错误),而不是针对我希望以特定方式处理的每种情况都在此函数中编写代码。
我是否错过了一个明显的解决方案?还是说我必须使用 Application_UnhandledException? [编辑] 如下所述,Error 属性正是我要查找的内容。让我困惑的是,异常被抛出并似乎未被捕获,但执行能够继续。它触发了 Application_UnhandledException 事件并导致 VS2008 中断执行,但在调试器中继续执行可以使执行继续进行。这不是真正的问题,只是看起来很奇怪。
9个回答

12

在服务方法完成事件处理程序中,我检查事件参数的Error属性。 我并没有遇到事件处理程序未被调用的问题。 如果服务器宕机,则调用需要几秒钟,然后返回Error属性中的ProtocolException。

假设您已尝试过这种情况,但确实从未调用回调函数,您可以尝试自定义生成的代理类。请参见此文章


只是为了澄清,“服务方法回调”意味着方法完成事件,而不是回调。回调与APM一起使用,而Silverlight使用EAP。 - Luke Puplett

5
我找到了一个讨论此问题的论坛帖子,其中提到最佳实践是使用Error属性。结合这个帖子和我的经验,我可以得出以下结论:
  • 在普通的.NET代码中,生成的代理类通过将异常放入Error属性中而正确处理异常。

  • 在Silverlight中,生成的代理类设置了Error属性,但并未完全处理异常。异常被调试器捕获,并弹出带有消息“ProtocolException was unhandled by user code”的异常框。尽管出现了这个消息,但似乎异常实际上并没有传递到Application_UnhandledException函数中。

我预计这是他们在最终版本中要修复的问题之一。

目前,我将使用Error属性并处理调试器断开执行的情况。如果这太烦人了,我可以关闭对ProtocolException异常的异常断点。


0

我不是水管工,所以我决定创建自己的WCF服务类,覆盖一些由Visual Studio自动生成的类文件“reference.cs”的功能。然后,我添加了自己的try/catch块来捕获通信错误。

我创建的类大致如下:

public class myWCFService : MyWCFServiceClient
{

    protected override MyController.MyService.IMyWCFService CreateChannel()
    {
        return new MyWCFServiceClientChannel(this);
    }

}

private class MyWCFServiceClientChannel : ChannelBase<MyController.MyService.IMyWCFService>, MyController.MyService.IMyWCFService
{
    /// <summary>
    /// Channel Constructor
    /// </summary>
    /// <param name="client"></param>
    public MyWCFServiceClientChannel(System.ServiceModel.ClientBase<MyController.MyService.IMyWCFService> client) :
    base(client)
    {
    }
    /// <summary>
    /// Begin Call To RegisterUser
    /// </summary>
    /// <param name="memberInformation"></param>
    /// <param name="callback"></param>
    /// <param name="asyncState"></param>
    /// <returns></returns>
    public System.IAsyncResult BeginRegisterUser(MyDataEntities.MembershipInformation memberInformation, System.AsyncCallback callback, object asyncState)
    {               
        object[] _args = new object[1];
        _args[0] = memberInformation;
        System.IAsyncResult _result = base.BeginInvoke("RegisterUser", _args, callback, asyncState);
        return _result;               
    }
    /// <summary>
    /// Result from RegisterUser
    /// </summary>
    /// <param name="result"></param>
    /// <returns></returns>
    public MyDataEntities.MembershipInformation EndRegisterUser(System.IAsyncResult result)
    {
        try
        {
            object[] _args = new object[0];
            MyDataEntities.MembershipInformation _result = ((MyDataEntities.MembershipInformation)(base.EndInvoke("RegisterUser", _args, result)));
            return _result;
        }
         catch (Exception ex)
        {
            MyDataEntities.MembershipInformation _result = new MyDataEntities.MembershipInformation();
            _result.ValidationInformation.HasErrors = true;
            _result.ValidationInformation.Message = ex.Message;
            return _result;
        }
    }
}

1
为什么要花费这么多的精力,只是为了丢弃除 Message 属性之外的所有异常部分呢?抛弃信息几乎从来都不是一个好主意。 - John Saunders
约翰说的。StackTrace和InnerException特别有用。 - MetalMikester

0

糟糕……

抱歉,我这边回答错误了(其实是微软的那个人没有选对答案,服务回调都在同一个UI线程上调用),事实是

更多信息:

- In development even detaching from the debugger, this method is never reached. 
- On the production environment yes.

我的猜测与Visual Studio选项和拦截异常有关。

更多信息,请参见此帖子http://silverlight.net/forums/p/48613/186745.aspx#186745

非常有趣的话题。


0

使用Silverlight 3,Visual Studio调试器会捕获这些异常,因此异常处理程序永远不会被触发,这可能会让人感到困惑。然而,在没有调试器的情况下运行时,异常处理程序会按预期被调用。我想只要我们意识到这一点,这就没问题了。我承认我浪费了几个小时来尝试深入了解Silverlight/Wcf/Browser的内部工作原理以找到我的异常。不要走弯路。


0

使用自定义的 WCF 代理生成器是处理 Silver-light 异步异常的好方法。点击这里下载源代码。


0

在WP7上使用XNA时,我发现我别无选择,只能手动添加try/catch到各种异步End*FunctionName*()方法中;其他任何尝试都无法防止当服务器不可用时导致应用程序崩溃和关闭。当服务更改时,手动更新此代码真的很麻烦。

我很惊讶这不是一个更大的问题,因为似乎在WP7上使用XNA没有其他捕获这些异常的方法,但我认为这更多地反映了有多少人(==不多)正在尝试这样做。如果他们只是让slsvcutil.exe生成同步方法,我们就可以轻松地在自己的工作线程中捕获这些方法,但不幸的是生成的代码使用线程池线程,没有办法捕获异常。


0

在异步客户端回调中,您可以忘记Application_UnhandledException,原因是:

只有在UI线程上触发的异常才能被Application.UnhandledExceptions捕获。

这意味着...对于WCF异步调用根本不会被调用 :-).

查看微软的详细响应

http://silverlight.net/forums/t/21828.aspx

你好,只有在 UI 线程上触发的异常才能被 Application.UnhandledExceptions 捕获。它无法捕获来自其他线程的异常。您可以尝试以下方法来解决问题:在 Visual Studio 中,从“调试”菜单中选择“异常”。然后勾选“常见语言运行时异常”。这将使调试器在抛出异常时停止。但请注意,有时这可能会非常烦人,因为即使异常已经被捕获,调试器仍会停止。您可以使用复选框来过滤您想要捕获的异常。

好消息是,在我的情况下,如果您不进行调试,则仅在客户端服务回调中处理错误消息就足够了。

谢谢

Braulio

3
这并非实际情况。当异常来自WCF服务时,我确实会遇到"Application_UnhandledExceptions"错误。我可以轻松检查抛出的FaultException,因此能够确定这一点。 - Rire1979

0
为了处理这种情况,请使用TargetInvocationException。当网络故障或服务不可用时,它将捕获异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接