2012-09-10 4 views
7

В настоящее время я работаю над приложением WPF с бэкэндом WCF. Мы внедрили решение для регистрации клиентов и решение для регистрации журналов для обработки исключений, и они отлично работают, но часто сложно связать информацию вместе по проводу. Если на сервере возникает исключение, мне нужен способ передать маркер исключения обратно через провод, чтобы я мог зарегистрировать его на клиенте с исключением. Таким образом, когда я затрудняюсь снять клиентскую ошибку, я могу очень легко сопоставить ее с исключением сервера.Ввод контракта WCF с использованием генератора динамических прокси Замок

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

Наша реализация WCF немного более надежна, чем готовый метод установки ссылки на службу. Мы реализовали динамическое создание прокси на клиенте. Мы достигли этого, создав интерфейсы веб-сервисов, которые совместно используются клиентом и сервером, и использовали класс Castle.DynamicProxy.ProxyGenerator и метод CreateInterfaceProxyWithoutTarget для создания прокси-сервера. Кроме того, когда мы вызываем метод CreateInterfaceProxyWithoutTarget, мы указываем реализацию IInterceptor. На сервере есть три основных класса, которые используются для нашего поведения следов и неисправностей:

FaultOperationInvoker (реализует IOperationInvoker): пытается вызвать метод сервиса с помощью IOperationInvoker.Invoke. Если это тип исключения, который он вызывает, если он является исключением, он пытается определить, существует ли контракт с дефектом, имеющий конкретный тип детали, и если да, тогда оберните и создайте новое исключение сбоя с подробной информацией.

internal class FaultOperationInvoker : IOperationInvoker 
    { 
     IOperationInvoker innerOperationInvoker; 
     FaultDescription[] faults; 

     public FaultOperationInvoker(IOperationInvoker invoker, FaultDescription[] faults) 
     { 
      this.innerOperationInvoker = invoker; 
      this.faults = faults; 
     } 

     #region IOperationInvoker Members 

     object[] IOperationInvoker.AllocateInputs() 
     { 
      return this.innerOperationInvoker.AllocateInputs(); 
     } 

     object IOperationInvoker.Invoke(object instance, object[] inputs, out object[] outputs) 
     { 
      try 
      { 
       return this.innerOperationInvoker.Invoke(instance, inputs, out outputs); 
      } 
      catch (FaultException e) 
      { 
       ServerLogger.GetLogger(instance.GetType().FullName).LogException(e, "Unhandled exception in service operation."); 

       //allow fault exceptions to bubble out 
       throw; 
      }   
      catch (Exception e) 
      { 
       Type exceptionType = e.GetType(); 

       ServerLogger.GetLogger(instance.GetType().FullName).LogException(e, "Unhandled exception in service operation."); 

       //if the excpetion is serializable and there operation is tagged to support fault contracts of type WcfSerivceFaultDetail 
       if (faults != null && (faults.OfType<WcfServiceFaultDetail>().Any() || faults.Count(x => x.DetailType == typeof(WcfServiceFaultDetail)) > 0)) 
       { 
        throw new FaultException<WcfServiceFaultDetail>(new WcfServiceFaultDetail(true, e), "Unhandled exception during web service call.", WcfFaultCustomExceptionFactory.GetFaultCodeForExceptionType(exceptionType)); 
       } 
       else 
       { 
        throw new FaultException("Unhandled exception during web service call.", WcfFaultCustomExceptionFactory.GetFaultCodeForExceptionType(exceptionType)); 
       } 

      } 
     } 

FaultOperationBehavior (Реализует IOperationBehavior) указывает операцию отправки вызова значение операции неисправность вызывающего, описанной выше.

[AttributeUsage(AttributeTargets.Method)] 
    public class FaultOperationBehavior : System.Attribute, IOperationBehavior 
    { 
     #region IOperationBehavior Members 

     void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, 
      System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } 


     void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, 
      System.ServiceModel.Dispatcher.ClientOperation clientOperation) { } 

     void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, 
      System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) 
     {   
      dispatchOperation.Invoker = new FaultOperationInvoker(dispatchOperation.Invoker, operationDescription.Faults.ToArray()); 
     } 

     void IOperationBehavior.Validate(OperationDescription operationDescription) { } 

     #endregion 
    } 

ExceptionTraceBehavior (Inherits Атрибут, Реализует IServiceBehavior) для обработки исключений, которые реализуют IServiceBehavior. У нас также есть класс (FaultOperationBehavior)

[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, Inherited = true)] 
public class ExceptionTraceBehavior : Attribute, IServiceBehavior 
{ 
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     foreach (var ep in serviceDescription.Endpoints) 
     { 
      foreach (var operation in ep.Contract.Operations) 
      { 
       if (operation.Behaviors.Contains(typeof(FaultOperationBehavior))) 
        continue; 

       operation.Behaviors.Add(new FaultOperationBehavior()); 

       //Check to see if this operation description contains a wcf service fault detail operation. If it doesn't, add one. 
       if (operation.Faults != null && (operation.Faults.Count == 0 || operation.Faults.Count > 0 && operation.Faults.Count(x => x.DetailType == typeof(WcfServiceFaultDetail)) == 0)) 
       { 
        FaultDescription faultDescription = new FaultDescription(operation.Name); 
        //faultDescription.Name = "WcfServiceFaultDetail"; 
        //faultDescription.Namespace = ""; 
        faultDescription.DetailType = typeof(WcfServiceFaultDetail); 
        operation.Faults.Add(faultDescription); 
       } 
      } 
     } 
    } 

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
    } 
} 

Каждый из наших сервисных интерфейсов имеет конкретную реализацию. Все наши сервисы также наследуют наш базовый класс обслуживания, который украшен атрибутом ExceptionTrace.

Итак, теперь с информацией о форе, вот в чем проблема. Я хочу, чтобы в каждой сервисной операции был заключен контракт на неисправность с подробным типом WCFServiceFaultDetail, но я не хочу помещать атрибут FaultContract в каждую операцию обслуживания. Как вы можете видеть в ExceptionTraceBehavior, я понял, как программно добавить контракт о неисправности, и он отлично работает для добавления ошибки в операцию. Когда обычное старое исключение попадает в операционную invoker, он обнаруживает, что существует соответствующий контракт о неисправности и бросает новый FaultExcption. Однако, когда исключение задерживает клиент, оно попадает в код catch (FaultExcection fe) вместо кода catch (FaultException fe).

Однако, если вы удалите код, чтобы программно добавить контракт о неисправности, я украшаю каждую операцию с помощью [FaultContract (typeof (WcfServiceFaultDetail))], клиент ловит исключение, как ожидалось.

Единственное, что я могу понять, это то, что, поскольку прокси генерируется динамически из интерфейса, а не из WSDL или других метаданных, и на интерфейсе нет дефектного контракта, мой контракт по программному сбою не будучи заслуженным.

С этой мыслью я попытался выяснить, как добавить контракт с неисправностью в реализацию IInterceptor, но не имел успеха.

Так что я надеюсь, что кто-то уже сделал это и может предоставить некоторые детали. Любая помощь приветствуется.

+0

Для корреляции ошибок клиент/сервер вы просмотрели раскрытие идентификатора активности? – Paciv

+0

Я этого не сделал, я посмотрю на это. Спасибо за совет. – BernicusMaximus

ответ

0

Я не эксперт WCF, но у меня руки немного грязные.

Я думаю, что вы правы. Контракты сбоев разрешаются из метаданных сборки при создании ChannelFactory определенного типа. Поскольку ваши интерфейсы не украшены соответствующими атрибутами FaultContract, ваш клиент использует контракт по умолчанию без каких-либо подробностей.

Добавление атрибутов FaultContract к вашим методам интерфейса во время выполнения не будет работать.

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

Я никогда не делал этого, но я думаю, что это можно сделать, начиная с DefineDynamicAssembly AppDomain.

+0

Это на самом деле то, что мы делаем. Мы создаем ChannelFactory типа интерфейса службы. Я попытался добавить поведение непосредственно перед тем, как мы назовем метод CreateChannel, и это, похоже, не работает. – BernicusMaximus

+0

Я понимаю вашу точку зрения, но я не предлагаю этого. «Добавление атрибутов FaultContract к вашим методам интерфейса во время выполнения, вероятно, тоже не будет работать». На самом деле я предлагаю что-то другое. Я говорю, что одним из способов добиться того, что вы пытаетесь сделать, может быть то, что вы создаете типы, модули, метаданные и сборки во время выполнения, кэшируете их и используете их. Вот почему я указывал на DefineDynamicAssembly AppDomain. –

+0

Извините, я неправильно понял. Это очень интересная идея, но я не уверен в ее возможности для наших целей. – BernicusMaximus

 Смежные вопросы

  • Нет связанных вопросов^_^