Обработка ошибок на стороне клиента WCF

я потребляю неуклюжий сервер WCF, который иногда выдает различные исключения и дополнительно возвращает некоторые из своих ошибок как string. У меня вообще нет доступа к коду сервера.

я хочу переопределить внутренний метод вызова запроса WCF-client и обработать все внутренние исключения и жестко закодированные ошибки, возвращаемые сервером, и поднять 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 подкласс или по крайней мере его канал.
Мне нужен контроль над внутренним методом вызова запроса.

обновление

я прочитал это ответ, и выглядит это частично то, что я ищу, но мне интересно, есть ли способ прикрепить IErrorHandler только для кода потребителя (клиента), я хочу добавить его в ClientBase<TChannel> как-то экземпляр.

обновление

этой статья также выглядит очень многообещающей, но она не работает. Атрибут applied, похоже, не вступает в силу. Я не могу найти способ добавить IServiceBehavior на стороне клиента.

обновление

я попытался прикрепить IErrorHandler via IEndpointBehavior.ApplyClientBehavior вызов:

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

(clientRuntime является параметром), но исключения по-прежнему бросаются непосредственно пропуская MyErrorHandler.
ApplyDispatchBehavior не вызывается вообще.

вывод

мне нужно достичь двух аспектов:

  1. оберните все исключения, которые могут возникнуть в течение жизни BaseClient<TChannel> и решить, обрабатывать ли их или бросать их. Это должно позаботиться обо всех операциях (услуга, которую я потребляю, выставляет несколько десятки)
  2. проанализируйте все ответы сервера и выбросьте исключения для некоторых из них, чтобы они были перенаправлены, как в инструкции 1.

3 ответов


вы можете использовать и модифицировать обработка исключений WCF Proxy Generator, более конкретно, базовый класс, который его использует. Это основная идея (проверьте описание too) должен обеспечить устойчивость соединения путем улавливания ошибок соединения и повторной попытки неудачной операции. Как вы можете себе представить, для этого ему нужно уметь ловить брошенные исключения, а также проверять результат вызовов.

основная функциональность дается 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; часть для обработки исключений, как вам нужно, и, очевидно, значение resturn shoudl также будет проверено для ваших нужд. Кроме того, вы можете оставить логику повтора или выбросить ее, если не ожидаете проблем с подключением. Существует еще один вариант Invoke на void методы возвращения.

О, и, кстати, он также работает с дуплексными каналами, для них есть еще один базовый класс.

если вы не хотите использовать генератор (он может даже не работать в более новых версиях VS), то вы можете просто взять базовый класс, например, из здесь, и генерировать фактический класс реализации с T4 из вашего интерфейса службы.


Если служба не возвращает истинное исключение, а просто сообщение, вы, вероятно, хотите добавить ClientMessageInspector в качестве нового поведения клиента. Смотрите: https://msdn.microsoft.com/en-us/library/ms733786.aspx


Я закончил тем, что использовал что-то, основанное на ответах в этой вопрос.

он прилипает к сгенерированному клиентскому коду и позволяет вызывать операции в общем виде.

на код является неполным, не стесняйтесь раскошелиться и редактировать его. Пожалуйста, сообщите мне, если вы нашли какие-либо ошибки или сделали какие-либо обновления.

Это довольно громоздко, поэтому я просто поделюсь кодом использования:

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