如何使用 IAsyncResult 从 WCF 异步中的回调中抛出异常

How to throw an exception from callback in WCF Async using IAsyncResult

我在我的项目中使用 WCF 异步调用,并且我使用客户端异步方法。我有一个像下面这样的场景 -

  //Code in Business Layer and this method is called from Web layer 
    private void GetGeneralNews()
    {
        client.BeginGetGeneralNewsFeed(GeneralNewsCallback, null);
    }

    //Call Back Method
    private static void GeneralNewsCallback(IAsyncResult asyncResult)
    {
       string response = string.Empty;

       try
       {
          response = client.EndGetGeneralNewsFeed(asyncResult);
       }
       catch(Exception ex)
       {
          throw ex; // Here is the problem. It does not throw the exception to the web layer instead it will suppress the   error.
       }
    }

所以如上面的代码片段所示,它不会将异常从业务层抛出到 Web 层,因为它会在业务层本身中被抑制。

我查看了一些他们建议采用异步和等待方法的博客和网站,因为我有 .NET 4.0 框架并且我看到“生成基于任务的操作”选项已禁用。因此,如果有任何使用 "IAsyncResult" 的选项(在客户端开始和结束),请告诉我。如果还有其他方法也欢迎。

好心人帮助我。

谢谢。

1) 如果您已经使用异步 ("Begin..."/"End...") 方法生成了 WCF 客户端代码 - 那么您可以使用 TPL 的任务 API 来处理 WCF(ContinueWith 等) .) via Task.Factory.FromAsync (example) - 适合处理遗留的 IAsyncResult ("Begin..."/"End...") methods/APIs(虽然简单,你可以看到带有反射器的源代码);

2) 工具生成的客户端代码不是好的方法 - 相反,您可以编写自己的具有真正异步支持的通用客户端代理(不仅仅是使用后台线程)。 Here 是如何开始的好例子,你需要用基于任务的方法包装 "Begin..."/"End..." 方法(使用相同的 Task.Factory.FromAsync)并使用表达式树来摆脱基于字符串的服务方法调用(我无法分享我的 class 来源)。

或者您可以使用现有的解决方案,例如 this

3) 不要忘记 ConfigureAwait

编辑:

您不必生成基于任务的操作,使用异步服务操作方法生成WCF 客户端代码就足够了("Begin..."/"End...")。或者你甚至可以只有同步 WCF 合同! (见链接)。 TPL 在 .NET 4 中可用(没有 async/await 语法糖 - 这是 CSharp 5.0 语言功能)。使用它(ContinueWith 而不是 await + WhenAny、WhenAll)。我什至在 3.5 中通过 Microsoft Reactive Extensions v1.0.2856.0 使用它。 AFAIK Reactive Extensions 是 BCL 中包含的初始版本。 ParallelExtensionsExtras 也可能有用

这是一个示例应用程序,它显示 WCF 不会吞噬异常。如果您没有收到异常,它必须被您的服务器端代码吞噬。

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using WcfQ.QServiceReference;

namespace WcfQ
{
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class QService : IQService
{
    public void Foo()
    {
        throw new ApplicationException("Please catch this");
    }
}

[ServiceContract]
public interface IQService
{
    [OperationContract]
    void Foo();
}

class Program
{
    static private QServiceClient client;

    static void Main(string[] args)
    {
        ServiceHost host = new ServiceHost(typeof(QService), new Uri("http://localhost:20001/q"));
        AddWsdlSupport(host);
        host.AddServiceEndpoint(typeof (IQService), new WSHttpBinding(SecurityMode.None), "");
        host.Open();

        client = new QServiceClient();
        client.BeginFoo(FooCallback, null);
        Console.WriteLine("ready");
        Console.ReadKey();
    }

    private static void FooCallback(IAsyncResult asyncResult)
    {
        try
        {
            client.EndFoo(asyncResult);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Got the exception: " + ex.Message);
        }
    }

    static void AddWsdlSupport(ServiceHost svcHost)
    {
        ServiceMetadataBehavior smb = svcHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        // If not, add one
        if (smb == null)
            smb = new ServiceMetadataBehavior();
        smb.HttpGetEnabled = true;
        smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        svcHost.Description.Behaviors.Add(smb);
        // Add MEX endpoint
        svcHost.AddServiceEndpoint(
          ServiceMetadataBehavior.MexContractName,
          MetadataExchangeBindings.CreateMexHttpBinding(),
          "mex"
        );

    }
}

}

这是这个程序的输出:

ready 
Got the exception: Please catch this

无论如何,我使用 TPL(任务并行库)解决了这个问题。

我在上述方法中遇到问题的原因是我的新线程将无法识别调用该方法的主线程。所以使用 TPL 我让我的主线程等到我的其他线程完成工作并返回然后根据响应抛出异常。

希望对您有所帮助。