使用自定义异常处理程序EL WCF抛出FaultException

时间:2012-01-06 01:44:29

标签: wcf enterprise-library faultexception protocolexception

所以我试图在我的WCF服务中使用Enterprise Library来为我做一些与异常相关的工作。

我的想法是为“NullReferenceException”设置“自定义异常处理程序”,并在“自定义异常处理程序”中创建FaultException异常。

我的理解是,这个“新的”异常将使它成为可能,我将在客户端捕获它。

一些代码可以更好地理解:

WCF服务:

[ServiceContract(Name="MyService", ConfigurationName="MyNamespace.MyService")]
    [ExceptionShielding("PolicyName")]
    public interface IMyService
    {
        [OperationContract]
        [FaultContract(typeof(MyFaultContract))]
        string Method(String Param);
}

public class MyService : IMyService
    {
        public string Method(String Param)
        {
          throw new  NullReferenceException("code-created null message here");
        }
}

自定义异常处理程序:(企业库)

public class MyExceptionHandler : IExceptionHandler
    {
        public MyExceptionHandler(NameValueCollection collection) { }

        public MyExceptionHandler() { }

        public System.Exception HandleException(System.Exception exception, 
                              Guid handlingInstanceId)
        {
                MyFaultContract details = new MyFaultContract();
                if (exception is NullReferenceException)
                {
                    details.ErrorCode = MyFaultCode.NullReferenceException;
                    details.OriginalMessage = exception.Message;
                    details.MyMessage = "Null Reference exception here!";
                }
                return new FaultException<MyFaultContract>(details);
            }
}

将NullReferenceException映射到“自定义异常处理程序”的App Config文件:

<exceptionPolicies>
      <add name="PolicyName">
        <exceptionTypes>
          <add name="NullReferenceException" type="System.NullReferenceException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add type="MyExceptionHandler, MyApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="MyExceptionHandler" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>

最后是期望捕获此FaultException的客户端代码:

MyService.MyServiceClient client = new MyService.MyServiceClient();
            client.Open();
            try
            {
                string result = client.Method(string parameter);
            }

            catch (System.ServiceModel.FaultException<MyService.MyFaultContract> ex)
            {
                // where I think exception should end up
            }
            catch (System.ServiceModel.FaultException ex)
            {
                // general FaultException
            }

但我得到一个ProtocolException,声明需要回复操作:

  

收到操作'方法'操作''的回复消息。   但是,您的客户端代码需要操作   'http://tempuri.org/MyService/MethodResponse'。

我做错了什么?是否可以在“自定义异常处理程序”中返回(未显式抛出)自定义FaultContract的FaultException?

感谢任何建议。

更新 如您所见,“自定义异常处理程序”中的后处理操作设置为“ThrowNewException”。 如果我将其更改为“NotifyRethrow”,我就不再使用“ProtocolException”了! 相反,客户端捕获常规的“FaultException”(不是自定义类型)。

现在的问题是为什么原始的自定义类型的FaultException无法跨越线路。

更新2 我忘记提到的一件事是我的WCF服务在ServiceHost中运行,而不是在IIS下运行。所以基本上我有一个Windows服务,它创建一个ServiceHost并通过这个ServiceHost公开Service1接口。

我认为这可能与此问题有关的原因是Enterprise Library声称它处理整个应用程序中的异常,而不仅仅是在服务边界内。也许这会导致异常被抛出太晚?或者没有达到正确的水平?

更新3
谢谢你的帖子! 你是对的,我搞乱自定义处理程序的唯一原因是因为我想设置MyFaultCode值。 我尝试了你的建议 - 配置了2个处理程序。

首先是自定义处理程序并捕获NullReference异常。它抛出一个新的异常 - MyApplicationException,带有MyFaultContract字段。

然后我配置了第二个处理程序 - 内置的“Fault Contract Exception Handler”捕获MyApplicationException,创建新的FaultException并自动将MyFaultcontract从MyApplicationException映射到新创建的FaultException。

WCF客户端仍然捕获通用的FaultException,而不是自定义合同。

2 个答案:

答案 0 :(得分:3)

我会考虑使用自定义WCF IErrorHandler实现。它提供了一个集中处理服务方法中出现的所有未处理异常的地方。

在我看来,它比使用Ent Lib更干净,减少了1个依赖,并且你不需要在配置中进行错误映射,因此XML更少。

示例:

public class MyServiceErrorHandler : IErrorHandler
{
    /// <summary>
    /// Central error handling for WCF services.
    /// Whenever a service encounteres an unhandled exception, it will end up here.
    /// This method will log the error and continue with normal error processing.
    /// </summary>
    /// <param name="error">The unhandled exception thrown from a service method.</param>
    /// <returns>true if the exceptions has been handled, false if normal error processing should continue. This implementation will always return false.</returns>
    public virtual bool HandleError(Exception error)
    {
        return false; // returning false so that WCF will still process the exception as usual.
    }

    /// <summary>
    /// Create a custom Fault message to return to the client.
    /// </summary>
    /// <param name="error">The 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 Message object that is returned to the client, or service, in the duplex case.</param>
    public virtual 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;

        // pack the exception info into the Fault Contract
        MyFaultContract details = new MyFaultContract();
        if (exception is NullReferenceException)
        {
            details.ErrorCode = MyFaultCode.NullReferenceException;
            details.OriginalMessage = error.Message;
            details.MyMessage = "Null Reference exception here!";
        }
        var faultException = new FaultException<MyFaultContract>(details);

        // turn the fault contract into the response WCF Message.
        var messageFault = faultException.CreateMessageFault();
        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
}

当然,如果您有其他理由想要使用Ent Lib,那么请继续。你可能会以某种方式将它们捆绑在一起。

此链接还有一些关于错误处理的良好读数,包括添加自定义IErrorHandler行为:http://www.codeproject.com/KB/WCF/WCFErrorHandling.aspx

答案 1 :(得分:0)

我打算将此作为评论但是时间过长了。

您是否考虑过使用异常处理块异常屏蔽和故障合同异常处理程序?我认为它可以满足您的大部分需求。 http://msdn.microsoft.com/en-us/library/ff953192(v=PandP.50).aspx

方法是创建自己的处理程序,将所有属性映射到自定义异常类型。然后配置FaultContractExceptionHandler以将异常中的属性映射到错误契约中的属性。

如果您不需要为MyFaultCode设置值,则可以避免一起编写自定义处理程序。