WCF客户端错误处理

Shi*_*mmy 9 c# wcf wcf-client

我正在消耗一个笨重的WCF服务器,偶尔会抛出各种异常,并且还会返回一些错误string.我根本无法访问服务器代码.

我想覆盖内部WCF客户端请求调用方法并处理服务器返回的所有内部异常和硬编码错误,并在Fault发生错误时引发事件,伪:

class MyClient : MyServiceSoapClient
{
    protected override OnInvoke()
    {
        object result;
        try
        {
            result = base.OnInvoke();
            if(result == "Error")
            {
                //raise fault event
            }
        catch
        {
            //raise fault event
        }
    }        
}
Run Code Online (Sandbox Code Playgroud)

因此,当我打电话时myClient.GetHelloWorld(),它通过我的重写方法.

怎么能实现这一目标?
我知道我不必使用生成的客户端,但我不想再次重新实现所有的合同,我想使用生成的ClientBase子类或至少它的通道.
我需要的是控制内部请求调用方法.

更新

我读了这个答案,看起来它部分是我正在寻找的,但我想知道是否有办法只附加一个IErrorHandler消费者(客户端)代码,我想以ClientBase<TChannel>某种方式将它添加到实例.

更新

这篇文章看起来很有希望,但它不起作用.应用的属性似乎没有生效.我找不到添加IServiceBehavior到客户端的方法.

更新

我试图安装一个IErrorHandler通过IEndpointBehavior.ApplyClientBehavior调用:

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
  clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers
           .Add(new ErrorHandler());
}
Run Code Online (Sandbox Code Playgroud)

(clientRuntime是一个参数),但仍会直接跳过异常跳转MyErrorHandler.
ApplyDispatchBehavior完全没有被召唤.

结论

我需要实现两个方面:

  1. 包装在a的生命周期中可能发生的所有异常,BaseClient<TChannel>并决定是处理它们还是抛弃它们.这应该照顾所有操作(我正在消耗的服务暴露几十个)
  2. 解析所有服务器回复并为其中一些回复抛出异常,因此它们将在语句1中转发.

Tam*_*mas 5

您可以使用和修改异常处理WCF代理生成器,更具体地说,它是使用它的基类.它的基本思想(检查此描述)是通过捕获连接故障和重试失败的操作来提供连接弹性.可以想象,为此目的,它需要能够捕获抛出的异常,并且还可以检查调用的结果.

主要功能由ExceptionHandlingProxyBase<T>基类提供,您使用它而不是ClientBase<T>.这个基类有一个Invoke如下方法,你需要修改它.

简化Invoke:

protected TResult Invoke<TResult>(string operationName, params object[] parameters)                              
{                                                        
  this.Open();                              
  MethodInfo methodInfo = GetMethod(operationName);                              
  TResult result = default(TResult);                              
  try                              
  {                              
    this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait); 
    result = (TResult)methodInfo.Invoke(m_channel, parameters);                              
  }                              
  catch (TargetInvocationException targetEx) // Invoke() always throws this type                              
  {                              
    CommunicationException commEx = targetEx.InnerException as CommunicationException;                              
    if (commEx == null)                              
    {                              
      throw targetEx.InnerException; // not a communication exception, throw it                              
    }                              
    FaultException faultEx = commEx as FaultException;                              
    if (faultEx != null)                              
    {                              
      throw targetEx.InnerException; // the service threw a fault, throw it                              
    }                              

    //... Retry logic

  }
  return result;
}  
Run Code Online (Sandbox Code Playgroud)

您需要根据需要修改throw targetEx.InnerException;零件以处理异常,显然还会根据您的需要检查resturn值.除此之外,如果您不希望出现连接问题,您可以退出重试逻辑或将其丢弃.还有的另一种变体Invokevoid返回方式.

哦,顺便说一句,它也适用于双工通道,还有另一个基类.

如果您不想使用生成器(它甚至可能不适用于较新版本的VS),那么您可以从此处获取基类,并从服务接口生成带有T4的实际实现类.


Shi*_*mmy 1

我最终根据这个问题的答案使用了一些东西。

它坚持生成的客户端代码,并允许一般地调用操作。

代码不完整,请随意 fork 和编辑它如果您发现任何错误或进行任何更新,请通知我。

它相当庞大,所以我只分享使用代码:

using (var proxy = new ClientProxy<MyServiceSoapClientChannel, MyServiceSoapChannel>())
{
  client.Exception += (sender, eventArgs) =>
  {
    //All the exceptions will get here, can be customized by overriding ClientProxy.
    Console.WriteLine($@"A '{eventArgs.Exception.GetType()}' occurred 
      during operation '{eventArgs.Operation.Method.Name}'.");
    eventArgs.Handled = true;
  };
  client.Invoke(client.Client.MyOperation, "arg1", "arg2");
}
Run Code Online (Sandbox Code Playgroud)