7

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

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

考慮してください:それは実装SomeExceptionの 1 つで定義され、スローされました。OperationContract例外がスローされると、次のコードを使用してフォルトに変換されます。

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

これはエラーを文字列として送信しますが、クライアントは次のような一般的な例外をキャッチする必要があります。

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

ではない:

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

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

この動作を実装する方法についてのアイデアはありますか?

4

3 に答える 3

10

WCF では、クライアントが標準の .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);

クライアント:

try
{
    _client.SampleMethod();
}
catch (FaultException<MyFaultMessage> e)
{
    //Handle            
}
catch (FaultException<ExceptionDetail> exception)
{
    //Getting original exception detail if includeExceptionDetailInFaults = true
    ExceptionDetail exceptionDetail = exception.Detail;
}
于 2013-07-31T06:15:39.810 に答える
9

この記事は役立つかもしれません:

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

スローされる可能性のある例外を列挙したくない場合に、私が使用してある程度成功したアプローチは、サーバー側の IErrorHandler 動作とクライアント側の IClientMessageInspector を実装する 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
于 2013-08-29T13:44:19.010 に答える
0

FaultException には、例外処理に使用できる Code プロパティがあります。

try
{
...
}
catch (FaultException ex)
{
   switch(ex.Code)
   {
       case 0:
          break;
       ...
   }
}
于 2013-07-31T05:44:43.303 に答える