Generally this is used to log all exceptions and to explicitly convert exceptions to faults, the simple solution here only uses the basic FaultException, but can be easily extended to use FaultException<T>.
WCF provides the IErrorHandler interface for our implementation of this functionality, here is what I have found to work (note the delegates/Actions are not necesarry - I just prefer not to have to modify the ErrorHandler for new projects):
public class ErrorHandler : IErrorHandler { private readonly Action<exception> LogException; private readonly Action<message> LogFault; public ErrorHandler(Action<exception> logException, Action<message> logFault) { LogException = logException; LogFault = logFault; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { if (error is FaultException) // Thrown by WCF - eg request deserialization problems, could also be explicitly thrown in code { LogFault(fault); return; } var faultCode = new FaultCode("UnknownFault"); if (error is ArgumentOutOfRangeException) { faultCode = new FaultCode("ArgumentOutOfRange"); } var action = OperationContext.Current.IncomingMessageHeaders.Action; fault = Message.CreateMessage(version, faultCode, error.Message, action); LogFault(fault); } public bool HandleError(Exception error) { // Logging of exceptions should occur here as all exceptions will hit HandleError, but some will not hit ProvideFault LogException(error); return false; // false allows other handlers to be called - if none return true the dispatcher aborts any session and aborts the InstanceContext if the InstanceContextMode is anything other than Single. } }
Note that the error handler alone is not enough, we also need to create a IServiceBehavior that will add the IErrorHandler :
public class ErrorHandlerBehavior : IServiceBehavior { private readonly IErrorHandler handler; public ErrorHandlerBehavior(IErrorHandler errorHandler) { this.handler = errorHandler; } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<serviceendpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach(ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(handler); } } }
The behaviour can then be added either through the web.config file or programatically to the System.ServiceModel.ServiceHost.Description.Behaviors.
No comments:
Post a Comment