First of all, let's find out who the players are. We will be working in the 'ServiceModelLayer' with the 'Dispatcher' in the context of the service host and the 'Proxy' in the context of a client. Their job is to translate between WCF Message Objects and .Net Method Calls.
WCF runtime
This pair dispatcher/proxy follow a sequence of steps during which you can insert your own code for message transformation, serialization, parameters inspection and so on.
Here is a list of 'points' where you can plug in and insert your code by implementing the following interfaces.
Stage | Interceptor Interface | Description |
---|---|---|
Parameter Inspection | IParameterInspector | Called before and after invocation to inspect and modify parameter values. |
Message Formatting | IDispatchMessageFormatter IClientFormatter | Called to perform serialization and deserialization. |
Message Inspection | IDispatchMessageInspector IClientMessageInspector | Called before send or after receive to inspect and replace message contents. |
Operation Selection | IDispatchOperationSelector IClientOperationSelector | Called to select the operation to invoke for the given message. |
Operation Invoker | IOperationInvoker | Called to invoke the operation. |
So now we know what kind of extensions we have and whom they apply to. In order to apply an extension (from the above list) we need to set up a behavior. There are 4 types of behaviors in wcf, each of them sharing this set of methods:
Validate - Called just before the runtime is built—allows you to perform custom validation on the service description.
AddBindingParameters - Called in the first step of building the runtime, before the underlying channel is constructed—allows you to add parameters to influence the underlying channel stack.
ApplyClientBehavior - Allows behavior to inject proxy (client) extensions. Note that this method is not present on IServiceBehavior.
ApplyDispatchBehavior - Allows behavior to inject dispatcher extensions.
One exception is that IServiceBehavior doesn't have an ApplyClientBehavior method because service behaviors can't be applied to clients.
Types of Behaviors
Scope | Interface | Potential Impact | |||
Service | Endpoint | Contract | Operation | ||
Service | IServiceBehavior | ✗ | ✗ | ✗ | ✗ |
Endpoint | IEndpointBehavior | ✗ | ✗ | ✗ | |
Contract | IContractBehavior | ✗ | ✗ | ||
Operation | IOperationBehavior | ✗ |
My example comes from a silverlight application where I had to create a custom behavior in order to catch wcf exceptions. So to achieve this I used IDispatchMessageInspector to change the status code of the reply to 200 (OK) and the IEndpointBehavior because I wanted to apply this extension only to this level.
Show me some code! :)
Step 1. Create the extension implementing IDispatchMessageInspector
public class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
// Do nothing
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
// Here the response code is changed to 200.
property.StatusCode = System.Net.HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
#endregion
}
Step 2. Create the behavior and apply the entension
public class FaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
public override Type BehaviorType
{
get { return typeof(FaultBehavior); }
}
protected override object CreateBehavior()
{
return new FaultBehavior();
}
#endregion
}
Step 3. Declare the behavior extension in the service config file
Inside the <system.serviceModel> create the <extensions> element if does not exist.
<extensions>
<behaviorExtensions>
<add name="silverlightFaults" type="myservicename.FaultBehavior, myservicename, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
NOTE: the type must be the fully qualified name of the assembly/class that you are using and must be declared on a single line, otherwise will not work (WCF known issue)
<behavior name="myFaultBehavior">
<silverlightFaults />
</behavior>
Step 5. Crate an endpoint and assign behavior configuration attribute
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="MyHttpBinding"
contract="IMyContract"
behaviorConfiguration="myFaultBehavior">
</endpoint>
And that's it! You can inspect the exceptions that come from a wcf service by using e.Error property in the completed event handler.