【问题标题】:WCF client-side error-handlingWCF 客户端错误处理
【发布时间】:2015-11-16 11:48:31
【问题描述】:

我正在使用一个笨重的 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
        }
    }        
}

所以当我调用myClient.GetHelloWorld() 时,它会通过我被覆盖的方法。

如何做到这一点?
我知道我不必使用生成的客户端,但我不想再次重新实现所有合约,我想使用生成的ClientBase 子类或至少它的频道。
我需要的是控制内部请求调用方法。

更新

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

更新

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

更新

我尝试通过IEndpointBehavior.ApplyClientBehavior 调用附加IErrorHandler

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

clientRuntime是一个参数),但是直接跳过MyErrorHandler还是会抛出异常。
ApplyDispatchBehavior根本不被调用。

结论

我需要实现两个方面:

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

【问题讨论】:

  • 您是否有权访问该服务的 app.config 或 web.config?
  • @DavidP 我无权访问服务器代码。此外,我更喜欢以编程方式设置我的配置,而不是使用应用设置或应用配置。
  • 好的 - 我想我明白你现在想要做什么。服务是返回异常,还是成功返回字符串消息?
  • @DavidP 两者。我需要处理这两种情况,另外我还想在同一个地方处理服务器关闭或其他一般异常。
  • IOOperationInvoker 文章是解决您的问题的正确方法。您可以在客户端使用它。不要在客户端将其添加为 IServiceBehavior。将其添加为 IOperationBehavior 或 IEndpointBehavior。

标签: c# wcf wcf-client


【解决方案1】:

您可以使用和修改Exception Handling WCF Proxy Generator,更具体地说,是它使用的基类。它的基本思想(也检查this description)是通过捕获连接故障并重试失败的操作来提供连接弹性。可以想象,为此它需要能够捕获抛出的异常,并且还需要检查调用的结果。

主要功能由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;
}  

您需要修改throw targetEx.InnerException; 部分以根据需要处理异常,显然还应检查返回值以满足您的需要。除此之外,如果您不希望出现连接问题,您可以保留重试逻辑或将其丢弃。 Invoke 的另一个变体是 void 返回方法。

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

如果您不想使用生成器(它甚至可能在较新版本的 VS 中不起作用),那么您可以只使用来自 here 的基类,并使用来自的 T4 生成实际的实现类你的服务接口。

【讨论】:

  • 感谢您的努力。 BaseClient&lt;TChannel&gt;.Invoke 不是虚拟的。应该是,我的问题不会在这里。
  • 我知道它不是virtual,这就是为什么我建议使用ExceptionHandlingProxyBase&lt;T&gt; 而不是ClientBase&lt;T&gt; 作为您的客户端代理基类。而你的实际实现类可以由生成器生成,也可以自己生成。)
  • 哦,我没有意识到它是一个外部工具。您亲自使用过这个工具吗?我的时间很紧,无法安装新软件,只是在花了很多时间测试后感到失望。此外,我使用的是 VS 2015,看起来那个工具是 2009 年的。这还能用吗?
  • 我在几年前使用过它。我怀疑它是否会在 VS2015 中工作,这就是为什么我在 ExceptionHandlingProxyBase&lt;T&gt; 中包含了 link。因此,如果您决定自己进行代理生成,则可以重用它。
  • 我认为可以。例如在超时的情况下。
【解决方案2】:

如果服务没有返回真正的异常,而只是一条消息,您可能希望添加一个 ClientMessageInspector 作为新的客户端行为。请看:https://msdn.microsoft.com/en-us/library/ms733786.aspx

【讨论】:

  • 服务器返回错误信息,但经常出现异常。我需要一种集中的方式来处理请求服务时可能发生的所有异常,无论是未处理的服务器异常,还是服务器已关闭或其他任何异常。我需要一个地方来将它们全部替换为 FaultException,以便它们通过隧道连接到频道的 Fault 事件。
  • 我为我的问题添加了赏金。我需要这两样东西,拦截消息,处理所有类型的Exceptions。
【解决方案3】:

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

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

code 不完整,请随时 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");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多