14

我有一个使用 WCF 服务的 Silverlight 2 应用程序。因此,它对服务方法的所有调用使用异步回调。如果在这些调用之一之前或期间服务未运行、崩溃或网络中断等,则会按预期生成异常。问题是,我不知道如何捕捉这个异常。

  • 因为它是一个异步调用,所以我不能用 try/catch 块包装我的开始调用,并让它拾取在程序从该点继续运行之后发生的异常。

  • 因为服务代理是自动生成的,所以我不能在每个生成的调用 EndInvoke 的函数上放置一个 try/catch 块(异常实际出现的地方)。这些生成的函数也被调用堆栈中的外部代码包围,因此堆栈中也没有其他地方可以放置 try/catch。

  • 我不能将 try/catch 放在我的回调函数中,因为异常发生在它们被调用之前。

  • 我的 App.xaml.cs 中有一个 Application_UnhandledException 函数,它捕获所有未处理的异常。我可以使用它,但这似乎是一种混乱的方式。我宁愿将此函数保留用于真正意外的错误(也称为错误),而不是在我想以特定方式处理的每种情况下都使用此函数中的代码。

我错过了一个明显的解决方案吗?还是我使用 Application_UnhandledException 卡住了?

[编辑]
如下所述,Error 属性正是我想要的。让我陷入循环的是,异常被抛出并且似乎未被捕获,但执行能够继续。它触发 Application_UnhandledException 事件并导致 VS2008 中断执行,但在调试器中继续允许执行继续。这不是一个真正的问题,只是看起来很奇怪。

4

9 回答 9

12

我在服务方法完成事件处理程序中检查事件参数的错误属性。我没有遇到未调用事件处理程序的问题。在服务器出现故障的情况下,调用需要几秒钟,然后在 Error 属性中返回 ProtocolException。

假设您已经尝试过这个并且您的回调真的从未被调用,您可能会考虑自定义生成的代理类。见这篇文章

于 2008-09-18T17:49:32.070 回答
5

我找到了一个讨论这个的论坛帖子,它提到最佳实践是使用 Error 属性。在这个线程和我自己的经验之间,我可以得出以下结论:

  • 在正常的 .NET 代码中,生成的代理类通过将异常放入 Error 属性而不是抛出它来正确处理异常。

  • 在 Silverlight 中,生成的代理类会设置 Error 属性,但不会完全处理异常。异常被调试器拾取,它会弹出异常框,并显示消息“ProtocolException 未被用户代码处理”。尽管有此消息,但该异常似乎并未真正出现在 Application_UnhandledException 函数中。

我希望这是他们将在最终版本中修复的问题之一。

现在,我将使用 Error 属性并只处理调试器中断执行。如果它太烦人了,我可以关闭 ProtocolException 的异常中断。

于 2008-09-19T16:28:24.433 回答
0

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

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

这意味着......根本没有为 WCF 异步调用调用:-)。

查看 MSFT 的详细回复

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

您好,Application.UnhandledExceptions 只能捕获 UI 线程上触发的异常。它无法捕获来自其他线程的异常。您可以尝试这样做来解决问题:在 Visual Studio 中,从“调试”菜单中,选择“异常”。然后检查“公共语言运行时异常”。这将使调试器在抛出异常时停止。但是请注意,这有时可能会很烦人,因为即使已经捕获了异常。您可以使用 CheckBoxes 过滤要捕获的异常。

就我而言,好消息是,如果您不进行调试,仅在 clietn 服务回调中处理错误消息就足够了。

谢谢

Braulio
于 2009-03-11T21:45:23.410 回答
0

哎呀……

对不起,我这边的错误答案(好吧,MSFT 的人没有在同一个 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

很有趣的话题。

于 2009-03-12T06:56:45.473 回答
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;
        }
    }
}
于 2009-06-06T18:37:35.337 回答
0

使用 Silverlight 3,Visual Studio 调试器会捕获这些异常,因此永远不会到达异常处理程序——令人困惑的是。但是,在没有调试器的情况下运行时,会按预期调用异常处理程序。我想这没问题,只要有人意识到这一点。我承认我浪费了几个小时试图弄清楚如何深入了解 Silverlight/Wcf/Browser 的内部工作以解决我的异常。不要去那里。

于 2010-03-26T12:58:53.903 回答
0

使用自定义 WCF 代理生成器是在 Silver-light 中处理异步异常的一个很好的解决方案。单击此处下载源代码。

于 2011-05-24T12:17:09.073 回答
0

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

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

于 2011-06-03T07:30:47.590 回答
0

要处理这种情况,请使用 TargetInvocationException。当网络关闭或您的服务不可用时,这将捕获异常。

于 2012-08-24T05:49:49.037 回答