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

时间:2015-04-06 17:53:54

标签: 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
    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”(在客户端开始和结束),请告诉我。如果有任何其他方法也欢迎。

请有人帮助我。

感谢。

3 个答案:

答案 0 :(得分:2)

这是一个示例应用程序,它显示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

答案 1 :(得分:0)

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

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

或者您可以使用this等现有解决方案。

3)不要忘记ConfigureAwait

编辑:

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

答案 2 :(得分:0)

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

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

希望它有所帮助。