2013-07-31 32 views
7

基本的には、ProvideFaultメソッドを実装して、WCFサービスからのあらゆる種類の例外をキャッチしてクライアントに送信するためのIErrorHandlerインターフェイスを実装しています。IErrorHandlerを使用したWCF例外処理

しかし、私は1つの重大な問題に直面しています。すべての例外はクライアントにFaultExceptionとして送信されますが、これにより、クライアントがサービスで定義した特定の例外を処理できなくなります。

考慮:OperationContractの実装の1つで定義されてスローされているSomeException。例外がスローされると、その次のコードを使用して、障害に変換:これは文字列としてエラーを送信ん

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

を、クライアントはのような一般的な例外をキャッチすることがあります。

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

をそしてません:InvalidOperationExceptionが同様

SomeExceptionよう
try{...} 
catch(SomeException e){...} 

だけでなく、カスタム例外が、システム例外は、上記のプロセスを使用してキャッチすることはできません。

この動作を実装する方法についてのご意見はありますか?

答えて

9

あなたのクライアントは、標準の.NET例外に関する情報を持っている.NETアプリケーションではないかもしれないので、契約として記述特別な例外を使用することが望ましいです。 この場合、サービス内にFaultContractを定義してから、FaultExceptionクラスを使用できます。

サーバ側

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

また、サーバーがそれのFaultExceptionsに例外の詳細を返し、設定ファイルで指定することができますが、これは本番アプリケーションには推奨されません。

<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> 

その後、例外を処理するメソッドを書き直すことができます。

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

CLIENT:

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には、例外ハンドリングを使用できるCodeプロパティがあります。 WCFで

try 
{ 
... 
} 
catch (FaultException ex) 
{ 
    switch(ex.Code) 
    { 
     case 0: 
      break; 
     ... 
    } 
} 
7

この記事は役立つかもしれない:

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

私はかもしれない例外を列挙したくなかったとき、私はいくつかの成功を収めて使用してきたアプローチクライアントサイドのIClientMessageInspectorとICrientMessageInspectorのIErrorHandlerビヘイビアを実装するPassthroughExceptionHandlingBehaviorクラスを作成することです。 IErrorHandlerの動作は、例外をエラーメッセージにシリアル化します。 IClientMessageInspectorは逆シリアル化して例外をスローします。

この現象は、WCFクライアントとWCFサーバーの両方に適用する必要があります。設定ファイルを使用するか、契約に[PassthroughExceptionHandlingBehavior]属性を適用して、動作をアタッチできます。

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

どの方法火災のHandleError後:

ここで行動クラスですか? – KumarHarsh

+0

エラーハンドラをクライアント側にのみ添付できますか?これを可能にするのは、サービスレベルではなく、 'ClientBase '実装に接続したいのですか? – Shimmy

+0

確かに。私はこのようなヘルパーメソッドを使用します: var factory = new ChannelFactory (binding、endpointAddress); factory.Endpoint.EndpointBehaviors.Add(new PassthroughExceptionHandlingBehavior()); return factory.CreateChannel(); –

関連する問題