2012-09-10 18 views
7

kullanarak WCF hata sözleşmesini enjekte etme Şu anda WCF arka ucuna sahip WPF uygulaması üzerinde çalışıyorum. İstisna yönetimi için bir istemci günlüğü çözümü ve bir sunucu günlüğü çözümü uyguladık ve harika çalışıyorlar, ancak bu bilgileri tel üzerinden bir araya getirmek çoğu zaman zordur. Sunucuda bir özel durum ortaya çıkarsa, istisnai bir durumla istemcide oturum açabilmem için bir istisna belirtecini telin üzerinden geçirmenin bir yolunu istedim. Bu şekilde, bir istemci hatası çekerken sorun yaşadığımda, sunucu istisnasıyla kolayca ilişkilendirebilirim.Castle Dynamic Proxy Generation

Mimarimiz hakkında biraz daha fazla bilgi vermek istiyorum ve sonra sorunumu bildireceğim.

WCF uygulamamız, servis referansı belirleme için kullanıma hazır bir yöntemdir. İstemcide dinamik proxy üretimi gerçekleştirdik. Bunu, istemci ve sunucu tarafından paylaşılan web hizmeti arabirimleri oluşturarak gerçekleştirdik ve proxy'yi oluşturmak için Castle.DynamicProxy.ProxyGenerator sınıfını ve CreateInterfaceProxyWithoutTarget yöntemini kullandık. Ek olarak CreateInterfaceProxyWithoutTarget yöntemini çağırdığımızda, bir II denetçisinin uygulanmasını belirtiriz.

FaultOperationInvoker (uygular IOperationInvoker): Bizim iz ve hata davranışı için kullanılan üç ana sınıfları vardır sunucuda olan IOperationInvoker.Invoke kullanarak hizmet yöntemi çağırmak çalışır. Eğer bir hata istisnası tipiyse, bir istisna ise, belirli bir detay türüne sahip bir arıza sözleşmesi olup olmadığını belirlemeye çalışır ve eğer öyleyse, o zaman sarın ve ardından detay bilgisi ile yeni bir hata istisnası getirin.

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 (uygular IOperationBehavior), yukarıda tarif edilen hata işlemi invoker için gönderme işlemi invoker işaret eder.

[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 IServiceBehavior uygulayan özel durumları işlemek için (ınherits Özellik, Uygular IServiceBehavior). Biz de bir sınıf (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) 
    { 
    } 
} 

hizmet arayüzleri her biri somut bir uygulama vardır vardır. Tüm hizmetlerimiz, ExceptionTrace özelliğiyle dekore edilen temel hizmet sınıfımızı da devralır.

Şimdi, arka plan bilgisi ile, işte problem. Her hizmet işleminin, WCFServiceFaultDetail ayrıntı türünde bir hata sözleşmesi olmasını istiyorum, ancak her hizmet işleminde bir FaultContract özniteliği koymak istemiyorum. ExceptionTraceBehavior uygulamasında gördüğünüz gibi, hata sözleşmesini programsal olarak nasıl ekleyeceğimi anladım ve hatayı işleme eklemek için harika çalışıyor. Operasyonda normal bir istisna tutulduğunda, uygun bir arıza sözleşmesi olduğunu ve yeni bir Hata Tasarrufu attığını görür. Ancak, istisna müşteriye geri döndüğünde, catch (FaultException fe) kodu yerine catch (FaultExcection fe) koduna girer. programlı arıza sözleşmede eklemek için kod kaldırırsanız

Ancak, ben beklendiği gibi [FaultContract (typeof (WcfServiceFaultDetail))], istemci durum yakalar her hizmet işlemi süsleyin.

Anlayabileceğim tek şey, proxy'nin bir WSDL veya başka bir meta verisinden değil, arabirimden dinamik olarak üretilmesinden ve arabirimde bir hata sözleşmesi düzenlemesinin yapılmadığından, programatik hata sözleşmemimin onurlandırılmak.Bunu göz önüne aldığımızda düşünceyle

, ben IInterceptor uygulanmasında arıza sözleşme eklemek anlamaya çalıştım ama hiçbir başarı oldu.

Yani birileri bu yapmış ve bazı ayrıntılar sağlayabilir umuyorum. Herhangi bir yardım takdir edilir.

+0

İstemci/sunucu hatası korelasyonu için, Etkinlik Kimliği Yayılımı'na baktınız mı? – Paciv

+0

Ben yapmadım, ona bir bakacağım. Bahşiş için teşekkürler. – BernicusMaximus

cevap

0

WCF uzmanı değilim ama biraz kirli elime.

ben doğru olduğunu düşünüyorum. Bazı türlerde bir ChannelFactory oluştururken hata sözleşmeleri montaj meta verilerinden çözümlenir. Arabirimleriniz uygun FaultContract öznitelikleriyle süslenmediğinden, istemciniz ayrıntı içermeyen bir varsayılan hata sözleşmesi kullanır. FaultContract ekleme

muhtemelen ya çalışmaz zamanında arayüz yöntemlerine bağlıyor.

bir çözüm üretilmesi ve kanal fabrikaları generete için, çalışma zamanında dinamik türlerini kullanıyor olabilir.

Bunu daha önce hiç yapmadım ama bunun, AppDomain'in DefineDynamicAssembly öğesinden başlayarak yapılması gerektiğini düşünüyorum.

+0

Aslında yaptığımız şey bu. Servis arayüzü tipinin ChannelFactory'yi oluşturuyoruz. CreateChannel yöntemini çağırmadan hemen önce davranışı eklemeyi denedim ve bu işe yaramıyor. – BernicusMaximus

+0

Anlayışını anlıyorum ama bunu önermiyorum. "Çalışma zamanında arayüz yöntemlerine FaultContract özniteliklerini eklemek muhtemelen işe yaramıyor." Aslında farklı bir şey öneriyorum. Yapmaya çalıştığınız şeyi elde etmenin bir yolunun, çalışma zamanında türler, modüller, meta veriler ve derlemeler oluşturup bunları önbelleğe almanız ve bunları kullanmanız olabileceğini söylüyorum. İşte bu yüzden AppDomain'in DefineDynamicAssembly'ı işaret ediyordum. –

+0

Üzgünüz, yanlış anladım. Bu çok ilginç bir fikir ama amacımızın fizibilitesinden emin değilim. – BernicusMaximus