WCF: provide generic FaultException in IErrorHandler

北城余情 提交于 2019-12-04 21:54:33

问题


Some context: We have a custom XSD and generate the WSDL and C# code using WSCF.blue. The client side uses ChannelFactory<T> and shares the interface which includes all the attributes added by WSCF.blue to match what is in the XSD.

I'm trying to implement IErrorHandler.ProvideFault where it provides a generic FaultException<T>, but on the client side I'm getting back a non-generic FaultContract. This is what my ProvideFault method looks like:

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
    if (!(error is FaultException))
    {
        FaultException faultException = FaultExceptionFactory.CreateFaultException(error);
        MessageFault messageFault = faultException.CreateMessageFault();
        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
}

In each service method, if I do a try/catch with throw FaultExceptionFactory.CreateFaultException(ex) it works as expected, so I think the [FaultContract], factory, bindings, etc. are all correct. Just in case, this is how the factory works:

BusinessRuleFaultExceptionType businessRuleFaultException = new BusinessRuleFaultExceptionType();
BusinessRuleFaultException.Code = exception.Code.ToString();
BusinessRuleFaultException.Reason = exception.Message;
return new FaultException<BusinessRuleFaultExceptionType>(
    businessRuleFaultException,
    exception.Message,
    new FaultCode(exception.Code.ToString())
);

I think the issue is with how the message is created in IErrorHandler, maybe in CreateMessageFault(). I have read that the action should be faultException.Action instead of null, but in fact faultException.Action is null. Maybe this is causing the issue. I can set an action in the factory, but what should the action be and why wouldn't that show up with the manual throw?

Any other ideas what I might be missing?

Edit: I checked the WSDL and found the specific operation I was calling and its action:

<wsdl:operation name="MyMethod">
<wsdl:fault wsaw:Action="http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault" name="BusinessRuleFaultExceptionTypeFault" message="tns:..."/>

I tried hard coding the action: Message.CreateMessage(..., "http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault") and setting it to the .Action in the factory but that still didn't work.

Edit 2: The throw/catch generates the following XML which allows catching the generic exception on the client:

<s:Fault>
    <faultcode xmlns="">s:-100</faultcode>
    <faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
    <detail xmlns="">
        <BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <Code xmlns="http://myNamespace/Entitites">-100</Code>
            <Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
        </BusinessRuleFaultExceptionType>
    </detail>
</s:Fault>

The IHttpErrorHandler generates the following which goes to the non-generic FaultException:

<s:Fault>
    <faultcode xmlns="">s:-100</faultcode>
    <faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
    <detail xmlns="">
        <BusinessRuleFaultExceptionType xmlns="http://schemas.datacontract.org/2004/07/My.Dot.Net.Namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
            <codeField>-100</codeField>
            <reasonField>xxx</reasonField>
        </BusinessRuleFaultExceptionType>
    </detail>
</s:Fault>

Edit 3: If I add [DataContract] and [DataMember] to the BusinessRuleFaultExceptionType, then I get almost the correct XML:

<s:Fault>
    <faultcode xmlns="">s:-100</faultcode>
    <faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
    <detail xmlns="">
        <BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
            <Code>-100</Code>
            <Reason>xxx</Reason>
        </BusinessRuleFaultExceptionType>
    </detail>
</s:Fault>

It's missing the namespace for Code and Reason. At least I think this narrows it down. The regular serialization is using the [XmlType] and [XmlElement] while the IErrorHandler is using [DataContract] and [DataMember]. Unfortunately [DataMember] doesn't let you set the namespace, so I think the question now is how to use the XMLSerializer in IErrorHandler. This describes my issue, but the fix won't work for the reason stated above: http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html

Edit 4: I partially found the issue. We are using XmlSerializer, but since IErrorHandler is outside the scope of the operation, it reverts to the default DataContractSerializer. The solution is to either change your service to use DataContractSerializer everywhere, or manually choose XmlSerializer when creating the faults. These two articles provided what I needed:

http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html http://zamd.net/2008/08/15/serializing-faults-using-xmlserializer/

That gets me very close. It's the same as the working XML, except the exception type's namespace is missing:

<s:Fault>
    <faultcode xmlns="">s:-100</faultcode>
    <faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
    <detail xmlns="">
        <BusinessRuleFaultExceptionType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <Code xmlns="http://myNamespace/Entitites">-100</Code>
            <Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
        </BusinessRuleFaultExceptionType>
    </detail>
</s:Fault>

I assume it doesn't add xmlns="http://myNamespace/Services" because it doesn't have the request's context. That namespace is defined in the interface, but not data contract. Am I really going to be forced to stay within the request's context (hopefully IOperationInvoker works), instead of using IHttpHandler?


回答1:


I don't have any experience with WSCF.blue therefore I tried to make an example application to demonstrate a working scenario using standard WCF client and server. Maybe it will help you find the missing connections to make your scenario work.

I used your BusinessRuleFaultExceptionType with Code and Reason properties. The BusinessRuleFaultExceptionType is a WCF fault contract.

I was a bit lazy and implemented all the code in a single console application. Wcf client uses the same Datacontracts and ICalculator interface as the Wcf service. Sorry for the code. It will be a long post.

First the Datacontracts and service Interface

using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

[ServiceContract(Namespace = "http://UE.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Add(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Subtract(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Multiply(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Divide(double n1, double n2);
}

/// <summary>
/// General fault structure. 
/// </summary>
[DataContract(Namespace = "http://someurl.temp")]
public sealed class BusinessRuleFaultExceptionType
{
    [DataMember]
    public int Code { get; set; }

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

Now, the service implementation:

[ErrorBehavior(typeof(MyErrorHandler))]
public class CalculatorService : ICalculator
{
    public double Add(double n1, double n2)
    {
        double result = n1 + n2;
        Console.WriteLine("Received Add({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        throw new ArgumentException("My exception");
        return result;
    }

    public double Subtract(double n1, double n2)
    {
        double result = n1 - n2;
        Console.WriteLine("Received Subtract({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }

    public double Multiply(double n1, double n2)
    {
        double result = n1 * n2;
        Console.WriteLine("Received Multiply({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }

    public double Divide(double n1, double n2)
    {
        double result = n1 / n2;
        Console.WriteLine("Received Divide({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }
}

And client implementation:

public class Client : ClientBase<ICalculator>, ICalculator
{

    public double Add(double n1, double n2)
    {
        try
        {
            return base.Channel.Add(n1, n2);
        }
        catch (FaultException<BusinessRuleFaultExceptionType> ex)
        {
            Console.WriteLine("This is my Code: {0}. This is the reason: {1}", ex.Detail.Code, ex.Detail.Reason);
        }
        catch (Exception ex)
        {
            throw;
        }
        return 0;
    }

    public double Subtract(double n1, double n2)
    {
        throw new NotImplementedException();
    }

    public double Multiply(double n1, double n2)
    {
        throw new NotImplementedException();
    }

    public double Divide(double n1, double n2)
    {
        throw new NotImplementedException();
    }
}

Main program to demonstrate this example

internal class Program
{
    private static void Main(string[] args)
    {
        ServiceHost myServiceHost = new ServiceHost(typeof(CalculatorService));

        // Open the ServiceHostBase to create listeners and start listening for messages.
        myServiceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();

        Console.ReadLine();

        Console.WriteLine("Sending data from client to service.");
        Client c = new Client();
        var res = c.Add(1, 2);

        Console.ReadLine();
    }

}

Implementation of error handling:

/// <summary>
/// Helper class for exception repackaging.
/// </summary>
internal class MyExceptionHandler
{
    /// <summary>
    /// Handles thrown exception into internal exceptions that are being sent over to client.
    /// </summary>
    /// <param name="error">Exception thrown.</param>
    /// <returns>Repackaged exception.</returns>
    internal static Exception HandleError(Exception error)
    {
        // could do something here.
        return error;
    }
}

#region Behaviour
/// <summary>
/// Control the fault message returned to the caller and optionally perform custom error processing such as logging.
/// </summary>
public sealed class MyErrorHandler : IErrorHandler
{
    /// <summary>
    /// Provide a fault. The Message fault parameter can be replaced, or set to null to suppress reporting a fault.
    /// </summary>
    /// <param name="error">The <see cref="Exception"/> object thrown in the course of the service operation.</param>
    /// <param name="version">The SOAP version of the message.</param>
    /// <param name="fault">The <see cref="System.ServiceModel.Channels.Message"/> object that is returned to the client, or service, in the duplex case.</param>
    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        //If it's a FaultException already, then we have nothing to do
        if (error is FaultException)
            return;

        error = MyExceptionHandler.HandleError(error);

        var serviceDebug = OperationContext.Current.EndpointDispatcher.ChannelDispatcher.IncludeExceptionDetailInFaults;

        BusinessRuleFaultExceptionType f = new BusinessRuleFaultExceptionType
        {
            Code = -100,
            Reason = "xxx"
        };

        FaultException<BusinessRuleFaultExceptionType> faultException = new FaultException<BusinessRuleFaultExceptionType>(f, error.Message);
        MessageFault faultMessage = faultException.CreateMessageFault();
        fault = Message.CreateMessage(version, faultMessage, faultException.Action);
    }

    /// <summary>
    /// Enables error-related processing and returns a value that indicates whether the dispatcher aborts the session and the instance context in certain cases.
    /// </summary>
    /// <param name="error">The exception thrown during processing.</param>
    /// <returns>true if Windows Communication Foundation (WCF) should not abort the session (if there is one) and instance context if the instance context is not Single; otherwise, false. The default is false.</returns>
    public bool HandleError(Exception error)
    {
        // could use some logger like Nlog but as an example it will do.
        Console.WriteLine("Error occured. {0}", error);

        return true;
    }
}

/// <summary>
/// This attribute is used to install a custom error handler for a service
/// </summary>
public sealed class ErrorBehaviorAttribute : Attribute, IServiceBehavior
{
    /// <summary>
    /// Type of component to which this error handled should be bound
    /// </summary>
    private readonly Type errorHandlerType;

    /// <summary>
    /// Initializes a new instance of the ErrorBehaviorAttribute class.
    /// </summary>
    /// <param name="errorHandlerType">Type of component to which this error handled should be bound</param>
    public ErrorBehaviorAttribute(Type errorHandlerType)
    {
        this.errorHandlerType = errorHandlerType;
    }

    /// <summary>
    /// Type of component to which this error handled should be bound
    /// </summary>
    public Type ErrorHandlerType
    {
        get { return errorHandlerType; }
    }

    /// <summary>
    /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
    /// </summary>
    /// <param name="description">
    /// <para>Type: <see cref="System.ServiceModel.Description.ServiceDescription"/></para>
    /// <para>The service description.</para>
    /// </param>
    /// <param name="serviceHostBase">
    /// <para>Type: <see cref="System.ServiceModel.ServiceHostBase"/></para>
    /// <para>The service host that is currently being constructed.</para>
    /// </param>
    void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
    {
    }

    /// <summary>
    /// Provides the ability to pass custom data to binding elements to support the contract implementation.
    /// </summary>
    /// <param name="description">
    /// <para>Type: <see cref="System.ServiceModel.Description.ServiceDescription"/></para>
    /// <para>The service description.</para>
    /// </param>
    /// <param name="serviceHostBase">
    /// <para>Type: <see cref="System.ServiceModel.ServiceHostBase"/></para>
    /// <para>The host of the service.</para>
    /// </param>
    /// <param name="endpoints">
    /// <para>Type: <see cref="System.Collections.ObjectModel.Collection&lt;ServiceEndpoint&gt;"/></para>
    /// <para>The service endpoints.</para>
    /// </param>
    /// <param name="parameters">
    /// <para>Type: <see cref="System.ServiceModel.Channels.BindingParameterCollection"/></para>
    /// <para>Custom objects to which binding elements have access.</para>
    /// </param>
    void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
    {
    }

    /// <summary>
    /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
    /// </summary>
    /// <param name="description">
    /// <para>Type: <see cref="System.ServiceModel.Description.ServiceDescription"/></para>
    /// <para>The service description.</para>
    /// </param>
    /// <param name="serviceHostBase">
    /// <para>Type: <see cref="System.ServiceModel.ServiceHostBase"/></para>
    /// <para>The host that is currently being built.</para>
    /// </param>
    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandler;

        try
        {
            errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
        }
        catch (MissingMethodException e)
        {
            throw new ArgumentException("The errorHandlerType specified in the ErrorBehaviorAttribute constructor must have a public empty constructor.", e);
        }
        catch (InvalidCastException e)
        {
            throw new ArgumentException("The errorHandlerType specified in the ErrorBehaviorAttribute constructor must implement System.ServiceModel.Dispatcher.IErrorHandler.", e);
        }

        foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
            channelDispatcher.ErrorHandlers.Add(errorHandler);
        }
    }
}

#endregion

And app.config of my console application:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:12345/service/calc" binding="basicHttpBinding" contract="ConsoleApplication2.ICalculator" >
      </endpoint>
    </client>
    <services>
      <service name="ConsoleApplication2.CalculatorService" behaviorConfiguration="service">
        <endpoint address="http://localhost:12345/service/calc" binding="basicHttpBinding" contract="ConsoleApplication2.ICalculator" >
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:12345/service/calc" />
          </baseAddresses>
        </host>
      </service>

    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="service">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

I used WCF test client to sent this request:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://UE.ServiceModel.Samples/ICalculator/Add</Action>
  </s:Header>
  <s:Body>
    <Add xmlns="http://UE.ServiceModel.Samples">
      <n1>0</n1>
      <n2>1</n2>
    </Add>
  </s:Body>
</s:Envelope>

and got this response:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="sk-SK">My exception</faultstring>
      <detail>
        <BusinessRuleFaultExceptionType xmlns="http://someurl.temp" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <Code>-100</Code>
          <Reason>xxx</Reason>
        </BusinessRuleFaultExceptionType>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

When I called

Client c = new Client();
var res = c.Add(1, 2);

I caught FaultException<BusinessRuleFaultExceptionType> ex which I logged to console

Console.WriteLine("This is my Code: {0}. This is the reason: {1}", ex.Detail.Code, ex.Detail.Reason);

EDIT: I have changed namespaces in BusinessRuleFaultExceptionType and set the solution to use [XmlSerializerFormat(SupportFaults = true)].

Changed interface, datacontracts and service implementation:

[ServiceContract(Namespace = "http://UE.ServiceModel.Samples")]
[XmlSerializerFormat(SupportFaults = true)]
public interface ICalculator
{
    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Add(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Subtract(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Multiply(double n1, double n2);

    [OperationContract(IsOneWay = false)]
    [FaultContract(typeof(BusinessRuleFaultExceptionType))]
    double Divide(double n1, double n2);
}

/// <summary>
/// General fault structure. 
/// </summary>
[DataContract(Name = "BusinessRuleFaultExceptionType", Namespace = "http://someurl.temp")]
[XmlType("BusinessRuleFaultExceptionType", Namespace = "http://someurl.temp")]
public sealed class BusinessRuleFaultExceptionType
{
    //[DataMember]
    [XmlElement(IsNullable = false,Namespace = "http://namespaces2.url")]
    public int Code { get; set; }

    [XmlElement(IsNullable = false, Namespace = "http://namespaces2.url")]
    public string Reason { get; set; }
}

[ErrorBehavior(typeof(MyErrorHandler))]
public class CalculatorService : ICalculator
{
    public double Add(double n1, double n2)
    {
        double result = n1 + n2;
        Console.WriteLine("Received Add({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);

        throw new FaultException<BusinessRuleFaultExceptionType>(new BusinessRuleFaultExceptionType()
        {
            Code = -100,
            Reason = "xxx"
        });
        //throw new ArgumentException("My exception");
        return result;
    }

    public double Subtract(double n1, double n2)
    {
        double result = n1 - n2;
        Console.WriteLine("Received Subtract({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }

    public double Multiply(double n1, double n2)
    {
        double result = n1 * n2;
        Console.WriteLine("Received Multiply({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }

    public double Divide(double n1, double n2)
    {
        double result = n1 / n2;
        Console.WriteLine("Received Divide({0},{1})", n1, n2);
        Console.WriteLine("Return: {0}", result);
        return result;
    }
}

I have found an article about the reasons why there are problems with using XmlSerializer in IErrorHandler. Therefore I have changed the service implementation to throw FaultException in method implementation and to not to rely on IErrorHandler.

I also found another (relatively old) article how to use XmlSerializer in IErroHandler and after some time I have made it possible to serialize even from IErrorHandler. I changed the trown exception back to ArgumentException. Here are the changes (I continued with previous example so maybe not all code and attributes are required):

[DataContract(Name = "BusinessRuleFaultExceptionType", Namespace = "http://someurl.temp")]
[XmlType("BusinessRuleFaultExceptionType", Namespace = "http://someurl.temp")]
[XmlRoot("BusinessRuleFaultExceptionType", Namespace = "http://someurl.temp")]
public sealed class BusinessRuleFaultExceptionType
{
    //[DataMember]
    [XmlElement(IsNullable = false, Namespace = "http://namespaces2.url")]
    public int Code { get; set; }

    [XmlElement(IsNullable = false, Namespace = "http://namespaces2.url")]
    public string Reason { get; set; }
}

public class XmlSerializerMessageFault : MessageFault
{
    FaultCode code;
    FaultReason reason;

    object details;

    public XmlSerializerMessageFault(FaultCode code, FaultReason reason, object details)
    {
        this.details = details;
        this.code = code;
        this.reason = reason;
    }

    public override FaultCode Code
    {
        get { return code; }
    }

    public override bool HasDetail
    {
        get { return (details != null); }
    }

    protected override void OnWriteDetailContents(System.Xml.XmlDictionaryWriter writer)
    {
        var ser = new XmlSerializer(details.GetType());
        ser.Serialize(writer, details);
        writer.Flush();
    }

    public override FaultReason Reason
    {
        get { return reason; }
    }
}

/// <summary>
/// Control the fault message returned to the caller and optionally perform custom error processing such as logging.
/// </summary>
public sealed class MyErrorHandler : IErrorHandler
{
    /// <summary>
    /// Provide a fault. The Message fault parameter can be replaced, or set to null to suppress reporting a fault.
    /// </summary>
    /// <param name="error">The <see cref="Exception"/> object thrown in the course of the service operation.</param>
    /// <param name="version">The SOAP version of the message.</param>
    /// <param name="fault">The <see cref="System.ServiceModel.Channels.Message"/> object that is returned to the client, or service, in the duplex case.</param>
    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {

        BusinessRuleFaultExceptionType f = new BusinessRuleFaultExceptionType
        {
            Code = -100,
            Reason = "xxx"
        };

        // create a fault message containing our FaultContract object
        FaultException<BusinessRuleFaultExceptionType> faultException = new FaultException<BusinessRuleFaultExceptionType>(f, error.Message);
        MessageFault faultMessage = faultException.CreateMessageFault();

        var msgFault = new XmlSerializerMessageFault(faultMessage.Code, faultMessage.Reason, f);

        fault = Message.CreateMessage(version, msgFault, faultException.Action);
    }

    /// <summary>
    /// Enables error-related processing and returns a value that indicates whether the dispatcher aborts the session and the instance context in certain cases.
    /// </summary>
    /// <param name="error">The exception thrown during processing.</param>
    /// <returns>true if Windows Communication Foundation (WCF) should not abort the session (if there is one) and instance context if the instance context is not Single; otherwise, false. The default is false.</returns>
    public bool HandleError(Exception error)
    {
        // could use some logger like Nlog but as an example it will do.
        Console.WriteLine("Error occured. {0}", error);

        return true;
    }
}

In both cases the serialized fault was:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="sk-SK">My exception</faultstring>
      <detail>
        <BusinessRuleFaultExceptionType xmlns="http://someurl.temp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Code xmlns="http://namespaces2.url">-100</Code>
          <Reason xmlns="http://namespaces2.url">xxx</Reason>
        </BusinessRuleFaultExceptionType>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>



回答2:


I would suggest using IOperationInvoker instead of IErrorHandler. With IOperationInvoker:

  • You can use single try/catch statement. It will work as if all operations are wrapped in try/catch.
  • You won't need to manually construct resulting Message.

The implementation in your case may look like this:

public object Invoke(object instance, object[] inputs, out object[] outputs)
{
    try
    {
        return _childInvoker.Invoke(instance, inputs, out outputs);
    }
    catch (Exception error)
    {
        throw FaultExceptionFactory.CreateFaultException(error);
    }
}


来源:https://stackoverflow.com/questions/23160426/wcf-provide-generic-faultexception-in-ierrorhandler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!