简体   繁体   English

WCF客户端错误处理

[英]WCF client-side error-handling

I'm consuming a clunky WCF server that occasionally throws various exceptions, and additionally returns some of its errors as string . 我正在消耗一个笨重的WCF服务器,偶尔会抛出各种异常,并且还会将一些错误作为string返回。 I have no access to the server code at all. 我根本无法访问服务器代码。

I want to override the inner WCF-client request invocation method and handle all inner exceptions and hard-coded errors returned by the server and raise the Fault event if an error occurs, pseudo: 我想覆盖内部WCF客户端请求调用方法并处理服务器返回的所有内部异常和硬编码错误,并在发生错误时引发Fault事件,伪:

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

So that when I call myClient.GetHelloWorld() , it goes thru my overridden method. 因此,当我调用myClient.GetHelloWorld() ,它会通过我的重写方法。

How can this be achieved? 怎么能实现这一目标?
I know I don't have to use the generated client, but I don't want to re-implement all the contracts again, and I want to use the generated ClientBase subclass or at least its channel. 我知道我不必使用生成的客户端,但我不想再次重新实现所有合同,我想使用生成的ClientBase子类或至少使用它的通道。
What I need is control over the inner request call method. 我需要的是控制内部请求调用方法。

Update 更新

I read this answer , and looks it's partially what I'm looking for, but I'm wondering if there is a way to attach an IErrorHandler to the consumer (client) code only, I want to add it to the ClientBase<TChannel> instance somehow. 我看了这个答案 ,看起来它部分是我正在寻找的,但我想知道是否有办法将IErrorHandler仅附加到消费者(客户端)代码,我想将它添加到ClientBase<TChannel>实例不知何故。

Update 更新

This article also looks very promising but it doesn't work. 这篇文章看起来很有希望,但它不起作用。 The applied attribute doesn't seem to take effect. 应用的属性似乎没有生效。 I can't find a way to add IServiceBehavior to the client side. 我找不到将IServiceBehavior添加到客户端的方法。

Update 更新

I tried attaching an IErrorHandler via IEndpointBehavior.ApplyClientBehavior calling: 我试图安装一个IErrorHandler通过IEndpointBehavior.ApplyClientBehavior电话:

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

( clientRuntime is a parameter), but exceptions are still thrown directly skipping MyErrorHandler . clientRuntime是一个参数),但仍然会直接跳过异常跳过MyErrorHandler
ApplyDispatchBehavior isn't called at all. ApplyDispatchBehavior不调用ApplyDispatchBehavior

Conclusion 结论

I need to achieve two aspects: 我需要实现两个方面:

  1. Wrap all exceptions that might occur during the lifetime of a BaseClient<TChannel> and decide whether to handle them or throw them on. 包装在BaseClient<TChannel>的生命周期内可能发生的所有异常,并决定是处理它们还是将它们抛出。 This should take care of all operation (the service I'm consuming exposes few dozens) 这应该照顾所有操作(我正在消耗的服务暴露几十个)
  2. Parse all server-replies and throw exceptions for some of them, so they're forwarded as in statement 1. 解析所有服务器回复并为其中一些回复抛出异常,因此它们将在语句1中转发。

You could use and modify the Exception Handling WCF Proxy Generator , more specifically, the base class that it uses. 您可以使用和修改异常处理WCF代理生成器 ,更具体地说,它是使用它的基类。 It's basic idea (check this description too) is to provide connection resilience by catching connection faults, and retrying the failed operation. 它的基本思想(检查此描述 )是通过捕获连接故障和重试失败的操作来提供连接弹性。 As you can imagine, for this purpose it needs to be able to catch thrown exceptions, and also, it can inspect the result of calls. 可以想象,为此目的,它需要能够捕获抛出的异常,并且还可以检查调用的结果。

The main functionality is given by the ExceptionHandlingProxyBase<T> base class, which you use instead of the ClientBase<T> . 主要功能由ExceptionHandlingProxyBase<T>基类提供,您使用它来代替ClientBase<T> This base class has an Invoke method as follows, you'd need to modify that. 这个基类有一个Invoke方法,如下所示,你需要修改它。

Simplified 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;
}  

You'll need to modify the throw targetEx.InnerException; 你需要修改throw targetEx.InnerException; part to handle the exceptions as you need, and obviously the resturn value shoudl also be inspected for your needs. 根据需要处理异常的部分,显然还要根据您的需要检查resturn值。 Other then that you can leave the retry logic or throw it away if you don't expect connection problems. 除此之外,如果您不希望出现连接问题,您可以退出重试逻辑或将其丢弃。 There is another variant of the Invoke for void return methods. Invoke for void返回方法还有另一种变体。

Oh, and by the way, it works with duplex channels as well, there is another base class for those. 哦,顺便说一句,它也适用于双工通道,还有另一个基类。

If you don't want to use the generator (it might not even work in newer versions of VS), then you could just take the base class for example from here , and generate the actual implementation class with T4 from your service interface. 如果您不想使用生成器(它甚至可能不适用于较新版本的VS),那么您可以从此处获取基类,并从服务接口生成带有T4的实际实现类。

If the service isn't returning a true exception, but just a message, you probably want to add a ClientMessageInspector as a new client behavior. 如果服务未返回真正的异常,而只返回消息,则可能需要将ClientMessageInspector添加为新的客户端行为。 Please see: https://msdn.microsoft.com/en-us/library/ms733786.aspx 请参阅: https//msdn.microsoft.com/en-us/library/ms733786.aspx

I've ended up using something based on the answers in this question. 我最终根据这个问题的答案使用了一些东西。

It sticks to the generated client code, and allows invocation of the operations generically. 它坚持生成的客户端代码,并允许一般调用操作。

The code is incomplete, feel free to fork and edit it. 代码不完整,随意分叉和编辑它。 Please notify me if you found any bugs or made any updates. 如果您发现任何错误或进行任何更新,请通知我。

It's pretty bulky so I'll just share the usage code: 它非常笨重,所以我只是分享使用代码:

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");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM