所以我正试图在WCF服务中使用企业库来为我做一些与异常相关的工作。
我的想法是为"NullReferenceException"设置一个"Custom Exception Handler",并在"Custom Exception Handler"中创建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映射到"自定义异常处理程序"的应用程序配置文件:
<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,声明需要回复操作:
收到操作"Method"的回复消息,操作为"。但是,您的客户端代码需要操作'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,而不是自定义协定。
我会考虑使用自定义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
我本来打算对此发表评论,但时间太长了。
您有没有研究过将异常处理块异常屏蔽与故障合约异常处理程序一起使用?我认为它能满足你的大部分需求。http://msdn.microsoft.com/en-us/library/ff953192(v=PandP.50).aspx
方法是创建自己的处理程序,将所有属性映射到自定义异常类型。然后配置FaultContractExceptionHandler,将异常中的属性映射到故障约定中的属性。
如果您不需要为MyFaultCode设置值,您可能会避免同时编写自定义处理程序。