Mar 19, 2012

Wcf Message Inspector - IMessageInspector

You can use message inspector to log, inspect, modify or completely replace a message by implementing IClientMessageInspector (client side) or IDispatchMessageInspector (server side)
 #region IDispatchMessageInspector Implementation

 public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
 {
  //You can access the body of a Message only once, regardless of how it is accessed.
  //http://msdn.microsoft.com/en-us/library/ms734675.aspx
  request = LogMessage(request.CreateBufferedCopy(int.MaxValue));
  return null;
 }

 #endregion

 
Now this can be added through Endpoint/Service/Contract behavior
 
  public class LogMessageEndpointBehavior : Attribute, IEndpointBehavior
 {
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new LoggingMessageInspector());
        }

 }
 

Mar 1, 2012

WCF error handling using DataAnnotations Validator

ParameterInspector can also be used to validate DataMember in the data contract for the incoming request by using DataAnnotations Validator.

So suppose I have a DataContract defined something like this

 [DataContract]
    public class CompositeType
    {
        string stringValue = "Hello ";
        [DataMember]
        [Required(ErrorMessage = "Name is required")]
        [StringLength(20, MinimumLength = 1, ErrorMessage = "Name must have between 1 and 20 characters")]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

Refer to DataAnnotations Required and StringLength attribute. Now I want a generic Validator which inspects the parameter and does Validation using DataAnnotations.

 
 public class ValidatingParameterInspector : IParameterInspector
    {
        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState){}
  
        public object BeforeCall(string operationName, object[] inputs)
        {
   //So I loop through all the input parameters and validate it using DataAnnotations Validator
            foreach (var input in inputs)
            {
                if (input != null)
                {
                    ValidationContext context = new ValidationContext(input, null, null);
                    Validator.ValidateObject(input, context, true);
                }
            }
            return null;
        }
    }
And now you can add this to OperationBehavior or even endpoint behavior
 

 public class ValidatingParameterInspectorOperationBehavior : Attribute, IOperationBehavior
    {
        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation){}
        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters){}
        public void Validate(OperationDescription operationDescription){}
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            ValidatingParameterInspector validatingParameterInspector = new ValidatingParameterInspector();
            dispatchOperation.ParameterInspectors.Add(validatingParameterInspector);
        }
    } 
So now before call reaches to the operation it will be inspected by BeforeCall where it will fail validation and raise exception. You can also raise custom FaultException

WCF error handling by implementing IErrorHandler

By implementing IErrorHandler you can control the fault message which is sent to the caller and perform error processing such as logging
  

 public class MyCustomeErrorHandler : IErrorHandler
    {

        public bool HandleError(Exception error)
        {
            Trace.TraceError(error.ToString());
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
   //Log server error for troubleshooting error
   
   //you can customise fault exception as you want
            FaultException faultException = new FaultException("Server error encountered.);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, faultException.Action);
        }

    }
We can now add this to ServiceBehavior
  

 public class MyCustomeErrorHandlerServiceBehavior : Attribute,IServiceBehavior
    {
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){}

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters){}

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            IErrorHandler handler = new MyCustomeErrorHandler();
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(handler);
            }
        }
    }
 
Now this way your custome error handler will be used where you can put some friendly message which will be sent to the caller.

The other way is to catch your application error in your operation and then throw FaultException somethign like this
 

 public string GetData(int value)
 {
  try
  {
   throw new Exception("My Service Operation Error");
  }
  catch (Exception ex)
  {
   //Log your applocation exception
   throw new FaultException(new ApplicationException("My Custome Fault Messahe"),new FaultReason("some fault reason"));
  }
 }