2013-07-31 27 views
7

Temel olarak, WCF hizmetinden her türlü özel durumu yakalamak ve ProvideFault yöntemini uygulayarak istemciye göndermek için IErrorHandler arabirimini uygularım.IErrorHandler kullanarak WCF özel durum işleme

Ancak önemli bir sorunla karşı karşıya geliyorum. Tüm istisnalar, istemciye FaultException olarak gönderilir, ancak bu, istemciyi hizmette tanımlamış olabileceği belirli istisnaları işlemek için devre dışı bırakır.

Göz önünde bulundurun: OperationContract uygulamalarından birinde tanımlanmış ve atılmış olan SomeException. istisna fırlatıldığında, onun Aşağıdaki kodu kullanarak bir arıza dönüştürüldü: Bu bir dize olarak hatayı gönderiyor

var faultException = new FaultException(error.Message); 
MessageFault messageFault = faultException.CreateMessageFault(); 
fault = Message.CreateMessage(version, messageFault, faultException.Action); 

ancak istemci gibi genel bir durum yakalamak zorundadır:

try{...} 
catch(Exception e){...} 

değil: ınvalidoperationexception gibi

SomeException gibi
try{...} 
catch(SomeException e){...} 

sadece özel istisnalar ama sistem istisnalar yukarıdaki işlem kullanılarak yakalandı edilemez.

Bu davranışı nasıl uygulayacağınıza dair herhangi bir fikir var mı?

cevap

9

. Bunu yapmak için FaultContract hizmetinizi tanımlayabilir ve FaultException sınıfını kullanabilirsiniz.

SUNUCU YAN

[ServiceContract] 
public interface ISampleService 
{ 
    [OperationContract] 
    [FaultContractAttribute(typeof(MyFaultMessage))] 
    string SampleMethod(string msg); 
} 

[DataContract] 
public class MyFaultMessage 
{ 
    public MyFaultMessage(string message) 
    { 
     Message = message; 
    } 

    [DataMember] 
    public string Message { get; set; } 
} 

class SampleService : ISampleService 
{ 
    public string SampleMethod(string msg) 
    { 
     throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred.")); 
    }   
} 

Ayrıca, sunucu 's FaultExceptions istisna ayrıntıları döndürür yapılandırma dosyasında belirtebilirsiniz, ancak bu üretim uygulamasında önerilmez:

<serviceBehaviors> 
    <behavior> 
    <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> 
     <serviceDebug includeExceptionDetailInFaults="true"/> 
    </behavior> 
</serviceBehaviors> 

Bundan sonra, özel durumlar için yönteminizi yeniden yazabilirsiniz:

var faultException = error as FaultException; 
if (faultException == null) 
{ 
    //If includeExceptionDetailInFaults = true, the fault exception with details will created by WCF. 
    return; 
} 
MessageFault messageFault = faultException.CreateMessageFault(); 
fault = Message.CreateMessage(version, messageFault, faultException.Action); 

MÜŞTERİ:

try 
{ 
    _client.SampleMethod(); 
} 
catch (FaultException<MyFaultMessage> e) 
{ 
    //Handle    
} 
catch (FaultException<ExceptionDetail> exception) 
{ 
    //Getting original exception detail if includeExceptionDetailInFaults = true 
    ExceptionDetail exceptionDetail = exception.Detail; 
} 
0

FaultException, özel durum işleme yöntemini kullanabileceğiniz bir Code özelliğine sahiptir.

Müşteriniz standart .NET istisnaları hakkında bilgiler .NET uygulama olmayabilir çünkü sözleşmeler olarak tarif özel durumlar kullanmak arzu WCF olarak
try 
{ 
... 
} 
catch (FaultException ex) 
{ 
    switch(ex.Code) 
    { 
     case 0: 
      break; 
     ... 
    } 
} 
7

Bu makale yardımcı olabilir:

http://www.olegsych.com/2008/07/simplifying-wcf-using-exceptions-as-faults/

Bunu belki özel numaralandırma istemiyordu zaman bazı başarı ile kullandım bir yaklaşım atılmak, istemci tarafı için sunucu tarafı ve IClientMessageInspector için bir IErrorHandler davranışı uygulayan bir PassthroughExceptionHandlingBehavior sınıfı oluşturmaktır. IErrorHandler davranışı, özel durumu hata iletisine serileştirir. IClientMessageInspector, istisnayı kaldırır ve fırlatır.

Bu davranışı WCF istemcisine ve WCF sunucusuna eklemeniz gerekir. Yapılandırma dosyasını kullanarak ya da sözleşmenize [PassthroughExceptionHandlingBehavior] özelliğini uygulayarak davranışları ekleyebilirsiniz.

public class PassthroughExceptionHandlingBehavior : Attribute, IClientMessageInspector, IErrorHandler, 
    IEndpointBehavior, IServiceBehavior, IContractBehavior 
{ 
    #region IClientMessageInspector Members 

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
    { 
     if (reply.IsFault) 
     { 
      // Create a copy of the original reply to allow default processing of the message 
      MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue); 
      Message copy = buffer.CreateMessage(); // Create a copy to work with 
      reply = buffer.CreateMessage();   // Restore the original message 

      var exception = ReadExceptionFromFaultDetail(copy) as Exception; 
      if (exception != null) 
      { 
       throw exception; 
      } 
     } 
    } 

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     return null; 
    } 

    private static object ReadExceptionFromFaultDetail(Message reply) 
    { 
     const string detailElementName = "detail"; 

     using (XmlDictionaryReader reader = reply.GetReaderAtBodyContents()) 
     { 
      // Find <soap:Detail> 
      while (reader.Read()) 
      { 
       if (reader.NodeType == XmlNodeType.Element && 
        detailElementName.Equals(reader.LocalName, StringComparison.InvariantCultureIgnoreCase)) 
       { 
        return ReadExceptionFromDetailNode(reader); 
       } 
      } 
      // Couldn't find it! 
      return null; 
     } 
    } 

    private static object ReadExceptionFromDetailNode(XmlDictionaryReader reader) 
    { 
     // Move to the contents of <soap:Detail> 
     if (!reader.Read()) 
     { 
      return null; 
     } 

     // Return the deserialized fault 
     try 
     { 
      NetDataContractSerializer serializer = new NetDataContractSerializer(); 
      return serializer.ReadObject(reader); 
     } 
     catch (SerializationException) 
     { 
      return null; 
     } 
    } 

    #endregion 

    #region IErrorHandler Members 

    public bool HandleError(Exception error) 
    { 
     return false; 
    } 

    public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault) 
    { 
     if (error is FaultException) 
     { 
      // Let WCF do normal processing 
     } 
     else 
     { 
      // Generate fault message manually including the exception as the fault detail 
      MessageFault messageFault = MessageFault.CreateFault(
       new FaultCode("Sender"), 
       new FaultReason(error.Message), 
       error, 
       new NetDataContractSerializer()); 
      fault = Message.CreateMessage(version, messageFault, null); 
     } 
    } 

    #endregion 

    #region IContractBehavior Members 

    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     ApplyClientBehavior(clientRuntime); 
    } 

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime) 
    { 
     ApplyDispatchBehavior(dispatchRuntime.ChannelDispatcher); 
    } 

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) 
    { 
    } 

    #endregion 

    #region IEndpointBehavior Members 

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     ApplyClientBehavior(clientRuntime); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
    { 
     ApplyDispatchBehavior(endpointDispatcher.ChannelDispatcher); 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
    } 

    #endregion 

    #region IServiceBehavior Members 

    public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) 
    { 
     foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers) 
     { 
      ApplyDispatchBehavior(dispatcher); 
     } 
    } 

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

    #endregion 

    #region Behavior helpers 

    private static void ApplyClientBehavior(System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     foreach (IClientMessageInspector messageInspector in clientRuntime.MessageInspectors) 
     { 
      if (messageInspector is PassthroughExceptionHandlingBehavior) 
      { 
       return; 
      } 
     } 

     clientRuntime.MessageInspectors.Add(new PassthroughExceptionHandlingBehavior()); 
    } 

    private static void ApplyDispatchBehavior(System.ServiceModel.Dispatcher.ChannelDispatcher dispatcher) 
    { 
     // Don't add an error handler if it already exists 
     foreach (IErrorHandler errorHandler in dispatcher.ErrorHandlers) 
     { 
      if (errorHandler is PassthroughExceptionHandlingBehavior) 
      { 
       return; 
      } 
     } 

     dispatcher.ErrorHandlers.Add(new PassthroughExceptionHandlingBehavior()); 
    } 

    #endregion 
} 

#region PassthroughExceptionHandlingElement class 

public class PassthroughExceptionExtension : BehaviorExtensionElement 
{ 
    public override Type BehaviorType 
    { 
     get { return typeof(PassthroughExceptionHandlingBehavior); } 
    } 

    protected override object CreateBehavior() 
    { 
     System.Diagnostics.Debugger.Launch(); 
     return new PassthroughExceptionHandlingBehavior(); 
    } 
} 

#endregion 
+0

hangi yöntemi yangın HandleError sonra:

İşte davranış sınıfı var? – KumarHarsh

+0

Yalnızca istemci tarafında bir hata işleyici ekleyebilir miyim? Servis seviyesine değil, ClientBase 'uygulamasına eklemek istiyorum, bu mümkün mü? – Shimmy

+0

Elbette. Bunun gibi bir yardımcı yöntem kullanıyorum: var fabrika = new ChannelFactory (ciltleme, endpointAddress); fabrika.Endpoint.EndpointBehaviors.Add (yeni PassthroughExceptionHandlingBehavior()); dönüş fabrikası.CreateChannel(); –