C# 如何使用IAsyncResult在WCF异步中从回调引发异常
我在项目中使用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
//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版本中可用。所以我没有这个选择,你不必。见编辑后的答案。仔细阅读。