C# 如何使用IAsyncResult在WCF异步中从回调引发异常

C# 如何使用IAsyncResult在WCF异步中从回调引发异常,c#,web-services,wcf,asynchronous,async-await,C#,Web Services,Wcf,Asynchronous,Async Await,我在项目中使用WCF异步调用,并且使用客户端异步方法。我有一个如下的场景- //Code in Business Layer and this method is called from Web layer private void GetGeneralNews() { client.BeginGetGeneralNewsFeed(GeneralNewsCallback, null); } //Call Back Method pr

我在项目中使用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)如果您已经使用异步(“开始…”/“结束…”)方法生成了WCF客户端代码,那么您可以通过task.Factory.FromAsync()使用TPL的任务API来处理WCF(ContinueWith等)-这非常适合处理遗留的IAsyncResult(“开始…”/“结束…”)方法/API(尽管很简单,您可以通过reflector查看源代码)

2) 工具生成的客户机代码不是一种好的方法——相反,您可以编写自己的具有真正异步支持的通用客户机代理(而不仅仅是使用后台线程)。 这是一个很好的示例,您需要使用基于任务的方法(使用相同的task.Factory.fromsync)包装“Begin…”/“End…”方法,并使用表达式树来摆脱基于字符串的服务方法调用(我无法共享我的类源)

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

3) 别忘了

编辑:


您不必生成基于任务的操作,只需使用异步服务操作方法(“Begin…”/“End…”)生成WCF客户端代码即可。或者,您甚至可以只有同步WCF合同!(见链接)。TPL在.NET4中可用(没有异步/等待语法糖-这是CSharp5.0语言特性)。使用它(继续,而不是等待+WhenAny,WhenAll)。我甚至在3.5版本中通过Microsoft反应式扩展v1.0.2856.0使用了它。AFAIK反应性扩展是BCL中包含的初始版本。也可能有用

这里是一个示例应用程序,它表明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"
        );

    }
}

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

我在上述方法中遇到问题的原因是我的新线程将无法识别主线程,该方法是从哪个层调用的。因此,使用TPL,我让我的主线程等待其他线程完成任务并返回,然后根据响应抛出异常


希望有帮助。

检查链接可能会有所帮助您是说您的
catch
块根本没有捕获任何异常吗?如果在服务器端代码中抛出未处理的异常,则默认情况下,上面的
catch
块应捕获
FaultException
。因此,当调用方调用
GetGeneralNews
时,您希望调用方出现异常?在与@usr相同的行上,您希望在客户端看到什么?您想要SOAP错误吗?您想要错误集合吗?是否需要.NET例外?另外,是否可以包括提供BeginGetGeneralNewsFeed的服务的合同?正如我提到的,“生成基于任务的操作”仅在.NET的4.5版本中可用。所以我没有这个选择,你不必。见编辑后的答案。仔细阅读。