Extending WCF’s Client and Dispatcher Runtimes

Jesus Rodriguez
Pablo Cibraro
Tellago, Inc

Published: October 2009

Articles in this series

Introduction

The Client and Dispatcher runtimes together represent a fundamental architectural element of WCF. These runtimes are responsible for orchestrating a set of components that implement the message processing and operation invocation in the client and service side respectively. Specifically, functional areas such as message encoding, filtering, operation selection, and operation invocation are implemented within the client and service runtimes, diminishing the complexity from the developer.

From a WCF programming model standpoint, the client and dispatcher runtimes expose various extensibility points that can be used to modify their default behavior. Developers can leverage these extensibility points to customize various WCF capabilities such as encoding messages, intercepting parameters or selecting the operation to invoke on the service side. It is important to recognize that client and dispatcher runtimes don’t present identical feature sets. For instance, some capabilities, like message filtering, are only relevant on the service (dispatcher) side. However, other components, such as message or parameter inspectors, can be executed on both runtimes. The following figure provides a view of some of the fundamental components of the WCF client and dispatcher runtimes: 

Figure 1: Client-Dispatcher runtimes extensibility model

In a calling application, the client runtime is responsible for translating method invocations in application code into outbound messages, pushing them to the underlying channels, and translating results back into return values and out parameters. This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, operation selection, message encoding and other extensibility functionality.

In the service application, the dispatcher runtime is responsible for pulling incoming messages out of the underlying channels, translating them into method invocations in application code, and sending the results back to the caller. This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, message filtering, encoding and other extensibility functionality.

In this chapter we explore the specifics of implementing the different extensibility points of WCF’s client and dispatcher runtimes. Following the style presented throughout this paper, we have included an overview of the functionality of the different extensibility components as well as some of the real world scenarios on which they can be applied. Finally, we provide a sample implementation of the different extensibility points that developers can use as the basics of their own extensibility projects.

Message Encoder

Overview

Message Encoders are the WCF components responsible for converting a Message object (SOAP, XML, JSON) into a wire representation (bytes). On the WCF stack, encoders sit right above the transport channel and below the rest of the WCF pipeline components.

By default, WCF includes different message encoders that interpret some the most common Web Services encoding formats such as Text, MTOM or Binary. Additionally, developers can incorporate new encoding techniques by extending the System.ServiceModel.Channels.MessageEncoder class which abstracts the basic functionalities of a message encoder.

Scenarios

Developers should consider implementing custom encoders in scenarios that require a specific message encoding mechanism that can’t be addressed by the default bindings. The following list includes some of those scenarios:

  • Message Compression: Some scenarios require the use of proprietary algorithms to compress the messages exchanged between client and services. Let us take the example of a bank that has developed a custom binary format for encoding the transaction history of a client. In order to accept a message encoded in that format as part of their WCF services, developers can implement a custom message encoder that translates the proprietary binary format into a WCF message object.
  • Optimized file type encoding: The transmission of some binary file types such as Adobe PDF can be highly optimized by using a native encoding format instead of WCF’s binary encoder. Those custom encoding formats can be incorporated as part of a custom message encoder that can be natively used by WCF services.

Implementation

The fundamental steps of building a new message encoder relies on implementing the ReadMessage and WriteMessage methods of the System.ServiceModel.Channels.MessageEncoder abstract class. When a message is received by the transport channel, the runtime calls the ReadMessage operation to translate the received byte[] into a WCF Message object that can be processed by the other elements of the WCF pipeline. Similarly, when a message is ready to be sent, the runtime calls a WriteMessage operation in order to serialize the Message object into the corresponding byte[] that can be used by the transport channel. The following code illustrates the implementation of a custom encoder (for the simplicity of this sample, we have omitted the details regarding the bytes[]-Message transformation):

public class SampleMessageEncoder: MessageEncoder

    {

        private SampleMessageEncoderFactory factory;

        public SampleMessageEncoder(SampleMessageEncoderFactory factory)

        {

        }

        public override Message ReadMessage(Stream stream,

                                int maxSizeOfHeaders, string contentType)

        {

            //implementation omitted for brevity

            Message message= Message.CreateMessage(.....);

            return message;

        }

        public override ArraySegment<byte> WriteMessage(Message message,

                            int maxMessageSize, BufferManager bufferManager,

                            int messageOffset)

        {

            //implementation omitted for brevity

            ArraySegment<byte> byteArray = Encode message into a byte array…

            return byteArray;

        }

        public override void WriteMessage(Message message, Stream stream)

        {

            //implementation omitted for brevity

        }

        …Rest of the implementation omitted for brevity...

    }

Figure 2: Custom message encoder (the complete implementation can be found in the code samples attached to this paper)

In order to instantiate the custom encoder, we need to implement a custom System.ServiceModel.Channels.MessageEncoderFactory and override the Encoder property:

public class SampleMessageEncoderFactory: MessageEncoderFactory

 {

     private MessageEncoder encoder;

     internal SampleMessageEncoderFactory(string mediaType,

                                string charSet, MessageVersion version)

     {

       this.encoder = new SampleMessageEncoder(this);

     }

     public override MessageEncoder Encoder

     {

         get

         {

             return this.encoder;

         }

     }

    …Rest of the implementation omitted forbrevity...

 }

Figure 3: Custom message encoder factory Figure: Custom message encoder (the complete implementation can be found in the code samples attached to this paper)

Once we have implemented the message encoder factory, we need to add it to the WCF runtime.

We can accomplish this by implementing a binding element that inherits from the System.ServiceModel.Channels.MessageEncodingBindingElement class and overriding the CreateMessageEncoderFactory operation:

public class SampleMessageEncoderBindingElement:MessageEncodingBindingElement

    {

        public override MessageEncoderFactory CreateMessageEncoderFactory()

        {

            return new SampleMessageEncoderFactory();

        }

        public override BindingElement Clone()

        {

            return new SampleMessageEncoderBindingElement(this);

        }

        …The rest of the implementation has been omitted for brevity...

    }

Figure 4: Initializing a custom message encoder factory (the complete implementation can be found in the code samples attached to this paper)

At this point we can programmatically add the binding element as illustrated in the following code:

ICollection<BindingElement> bindingElements = new List<BindingElement>();

HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();

SampleMessageEncoderBindingElement sampleEncodingBindingElement = new SampleMessageEncoderBindingElement();

bindingElements.Add(sampleEncodingBindingElement);

bindingElements.Add(httpBindingElement);

CustomBinding binding = new CustomBinding(bindingElements);

Figure 5: Adding a custom message encoder programmatically

Alternatively, we can implement a binding extension element in order to enable a declarative mechanism to add the message encoding binding element:

public class SampleMessageEncoderBindingExtensionElement:

                                         BindingElementExtensionElement

    {

        public override Type BindingElementType

        {

            get { return typeof(SampleMessageEncoderBindingElement); }

        }

        protected override BindingElement CreateBindingElement()

        {

            return new SampleMessageEncoderBindingElement();

        }

    }

Figure 6: Custom message encoding binding extension element

The binding extension element can be added declaratively as illustrated in the following code:

<configuration>

  <system.serviceModel>

    <extensions>

      <bindingElementExtensions>

        <add name="customMessageEncoding"  type="Tellago.ServiceModel.Samples.MessagingExt.SampleMessageEncoderBindingExtensionElement, Tellago.ServiceModel.Samples.MessagingExt, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

      </bindingElementExtensions>

    </extensions>

    <services>

      <service name="Tellago.MessagingExtSamples.Service.SampleService"

               behaviorConfiguration="MtdBehavior">

        <host>

          <baseAddresses>

            <add baseAddress="https://localhost:9091/Services"/>

          </baseAddresses>

        </host>

        <endpoint address="/SampleService"

           binding="customBinding"

           bindingConfiguration="EncodingExtensions"

           contract="Tellago.MessagingExtSamples.Service.ISampleService"   />

      </service>

    </services>

    <bindings>

      <customBinding>

        <binding name="EncodingExtensions">

            <customMessageEncoding />

            <httpTransport  />

        </binding>

      </customBinding>

    </bindings>

  </system.serviceModel>

</configuration>

Figure 7: Registering a custom message encoder declaratively

Message Filters

Overview

WCF filters are components that inspect an incoming message and evaluate a set of Boolean predicates in order to make operational decisions about the message processing. Given the semantics of its functionality, message filters only execute on the service side and have no equivalent on the client side. WCF provides a set of default filters that address some of the most common filtering scenarios. For instance, the ActionMessageFilter evaluates a message based on a list of actions while the XPathMessageFilter uses a set of XPath predicates to filter the message base on its contents.

Filters are orchestrated by the WCF filtering system which is optimized for performance. More specifically, filters are grouped in filter tables that indicate what actions need to be executed in case the message matches a specific filter. A filter table is another WCF extensibility component abstracted by the IMessageFilterTable interface.

Scenarios

Custom filters are a great fit for scenarios that require executing custom logic that assert whether a message should be processed by the runtime.

  • Message validation: Some services require levels of message validation that go beyond what can be expressed in a contract. For instance, a WCF service that processes a financial transaction can use a filter that validates the transaction based on rules deployed in a business rules server. This type of scenario is particularly important in RESTful services that don’t expose any static contract validation. We can address these types of scenarios by implementing a custom message filter that implements the required message validation. In our example, the custom filter will interact with the business rules server in order to execute certain policies against the message.
  • Routing: Combining filters and filter tables developers can implement intelligent routing logic that distributes messages to services based on the criteria implemented by specific filters. For instance, we can use filter tables to route an incoming message to different endpoints based on the output of specific message filters. An example of this technique is the forwarding service included as part of WCF 4.0.

Implementation

WCF message filters are abstracted by the System.ServiceModel.Dispatcher.MessageFilter class. Specifically, the WCF runtime calls the Match method of this class in order to determine if a Message is compliant with the filter criteria. The following code illustrates a sample implementation of a filter:

public class SampleMessageFilter: MessageFilter

{

    public override bool Match(Message message)

    {

        …Implementation has been omitted for brevity…

        return true;

    }

    public override bool Match(MessageBuffer buffer)

    {

        …Implementation has been omitted for brevity…

        return true;

    }

}

Figure 8: Sample WCF message filter

As you can see, both overrides of Match returns a single Boolean parameter indicating whether a message is valid according to the filter’s criteria. Depending of the functionality implemented by the filter, a message filter can be deployed using different mechanisms. A very common technique is to initialize message filters as part of a WCF service or endpoint behavior. The following code shows a WCF Endpoint Behavior that replaces the default action filter by our sample filter:

public class MsgExtensibilityEndpointBehavior: IEndpointBehavior

{

  public void AddBindingParameters(ServiceEndpoint endpoint,

             BindingParameterCollection bindingParameters)

        { }

  public void ApplyClientBehavior(ServiceEndpoint endpoint,

                                                ClientRuntime clientRuntime)

        { }

 public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                                      EndpointDispatcher endpointDispatcher)

        {

            Console.WriteLine("Adding custom message filter...");

            endpointDispatcher.AddressFilter = new SampleMessageFilter();

        }

        public void Validate(ServiceEndpoint endpoint)

        { }

    }

Figure 9: Adding a WCF filter in an endpoint behavior

We can use similar mechanisms for initializing other types of message filters. Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Message Inspectors

Overview

Message inspectors are a WCF extensibility element that can be used in the service model's client runtime and dispatch runtime programmatically or through configuration. Message inspectors can inspect and alter messages after they are received or before they are sent. From a programming model standpoint, they are abstracted by the System.ServiceModel.Dispatcher.IClientMessageInspector and System.ServiceModel.Dispatcher.IDispatchMessageInspector interfaces respectively.  By implementing these symmetric interfaces we can develop message inspectors that work seamlessly across the client and dispatcher runtimes.

Scenarios

Given its generic functionality and the versatility of its programming model, WCF message inspectors can be applied ubiquitously across a large variety of scenarios like the ones listed below:

  • Tracking: Developers can implement WCF message inspectors for instrumenting services and clients by implementing custom tracking mechanisms. For instance, the WCF Business Activity Monitoring (BAM) interceptor included as part of BizTalk Server R2 is implemented as a WCF message inspector.
  • Transformation: WCF message interceptors are an ideal place to apply custom transformations to the incoming and outgoing message by executing XSLT or XQuery scripts.

Implementation

As explained in the previous section, the fundamental steps for building client or service message interceptors are implementing the System.ServiceModel.Dispatcher.IClientMessageInspector and System.ServiceModel.Dispatcher.IDispatchMessageInspector respectively.

A client inspector intercepts the request message before it is sent to the service and the response message before it gets processed by the client application. In that case, the programming logic is expressed as part of the BeforeSendRequest and AfterReceiveReply methods of the System.ServiceModel.Dispatcher.IClientMessageInspector interface.

Similarly, a dispatch inspector enables the interception of inbound or outbound messages either prior to dispatching a request message to an operation or before returning a reply message to a caller. Programmatically, developers should implement the AfterReceiveRequest and BeforeSendReply methods of the System.ServiceModel.Dispatcher.IDispatchMessageInspector interface.

The following code shows an implementation of a custom dispatch and client message inspector:

public class SampleDispatchMessageInspector: IDispatchMessageInspector,

                                             IClientMessageInspector

{

  public object AfterReceiveRequest(ref Message request,

                  IClientChannel channel, InstanceContext instanceContext)

  {

       …Implementation has been omitted for brevity…

       return null;

  }

  public void BeforeSendReply(ref Message reply, object correlationState)

  {

   …Implementation has been omitted for brevity…

 }

public object BeforeSendRequest(ref Message request,

                                                     IClientChannel channel)

{

    …Implementation has been omitted for brevity…

    return null;

}

public void AfterReceiveReply(ref Message reply,

                                                   object correlationState)

{

        …Implementation has been omitted for brevity…

}

}

Figure 10: Custom message inspector

The WCF runtime will call the Before…<> and After…<> methods to intercept the messages that are processed by the client and service applications.

Similar to other WCF extensibility components, we can inject this message interceptor into the WCF dispatcher runtime by implementing the corresponding behavior as illustrated in the following code:

public class MsgExtensibilityEndpointBehavior: IEndpointBehavior

{

 public void AddBindingParameters(ServiceEndpoint endpoint,

                      BindingParameterCollection bindingParameters)

 { }

 public void ApplyClientBehavior(ServiceEndpoint endpoint,

                                       ClientRuntime clientRuntime)

 { }

public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                                EndpointDispatcher endpointDispatcher)

{

  endpointDispatcher.DispatchRuntime.MessageInspectors.Add(

                                 new SampleDispatchMessageInspector());

}

public void Validate(ServiceEndpoint endpoint)

{ }

}

Figure 11: Instantiating a message inspector using an endpoint behavior

Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Message Formatter

Overview

As a message gets processed by WCF’s client and dispatcher runtimes it has to eventually be translated into objects that can be manipulated by the service or client application. These processes are commonly known as serialization and deserialization and are handled by a component of WCF’s runtime known as message formatters.

Typically, message formatters rely on an existing serializer such as DataContractSerializer or the XmlSerializer to transform an incoming or outgoing message into objects that can be interpreted by WCF’s applications. However there are scenarios on which we need specialized conversions between messages and objects that can’t be accomplished by the default serializers. We can address those scenarios by extending WCF’s serialization runtime implementing a custom message formatter.

 From a programming model standpoint, message formatters are abstracted by the System.ServiceModel.Dispatcher.IDispatchMessageFormatter and System.ServiceModel.Dispatcher.IClientMessageFormatter depending on whether the formatting logic executes on the service or client side respectively. Developers can consider using these interfaces for implementing additional mechanisms for converting a WCF message into objects that cannot be achieved with the default serializers.

Scenarios

Implementing custom message formatters allows developers to extend the WCF infrastructure to accept different message schemes. In that sense, custom message formatters are applicable in a variety of scenarios as illustrated in the following list:

  • Custom message formats: By implementing a custom message formatter developers can abstract the translation from objects to specific message formats different from the traditional XML or SOAP. For instance a custom message formatter can serialize-deserialize objects into a .pdf document that can be encoded in a WCF message object.
  • Extending default serializers: Even when using the default serializers (Xml, Data Contract), there are scenarios that require an extra level of customization in terms of the message-to-object transformation. For instance, some scenarios might require some alteration of the value of a certain object’s properties based on the contents of the corresponding message. Scenarios like those can be easily addressed by the use of a custom message formatter that extends one of the default formatters.

Implementation

As explained in the previous sections, WCF abstracts a message formatter by using the System.ServiceModel.Dispatcher.IDispatchMessageFormatter and System.ServiceModel.Dispatcher.IClientMessageFormatter interfaces depending on whether the formatting logic should be executed on the service or client side respectively. Both interfaces include methods for converting a WCF Message into objects and vice versa. The following code illustrates a sample implementation of a dispatch message formatter:

public class SampleDispatchMessageFormatter: IDispatchMessageFormatter

{

  public void DeserializeRequest(Message message, object[] parameters)

  {

       XmlDictionaryReader reader = message.GetReaderAtBodyContents();

       XDocument xBodyDoc= XDocument.Parse(reader.ReadOuterXml());

       parameters[0]= xBodyDoc.Root.Descendants(XName.Get("msg",

                       "http://tempuri.org/")).First<XElement>().Value;

  }

  public Message SerializeReply(MessageVersion messageVersion,

                                     object[] parameters, object result)

  {

     Console.WriteLine("Message Formatter: Executing SerializeReply

                                                             operation...");

     XmlNodeReader bodyReader = WriteResponseMessage((string)result);

     Message response= Message.CreateMessage(messageVersion,

              "http://tellago.servicemodelsamples/EchoResponse", bodyReader);

            return response;  }

}

Figure 12: Sample dispatch message formatter implementation (the complete implementation can be found in the code samples attached to this paper)

At runtime, WCF invokes the DeserializeRequest method to transform an incoming message into a set of objects that should be passed to the service’s operation. Similarly, the SerializeReply method isused by the WCF runtime to construct the message that should be sent back the client based on the output of an operation. WCF provides an equivalent programming model for the client message formatter.

We can inject our custom message formatter into the WCF runtime by assigning it to the Formatter property of the DispatchOperation object. We can accomplish this by implementing the corresponding type of behavior. The following sample shows how to accomplish this by implementing a WCF endpoint behavior:

public class MsgExtensibilityEndpointBehavior: IEndpointBehavior

{

  public void AddBindingParameters(ServiceEndpoint endpoint,

                     BindingParameterCollection bindingParameters)

  { }

  public void ApplyClientBehavior(ServiceEndpoint endpoint,

                                      ClientRuntime clientRuntime)

  { }

  public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                            EndpointDispatcher endpointDispatcher)

  {

   SampleDispatchMessageFormatter formatter = new

                                   SampleDispatchMessageFormatter();

   foreach (DispatchOperation operation in

                      endpointDispatcher.DispatchRuntime.Operations)

      operation.Formatter = formatter;

  }

  public void Validate(ServiceEndpoint endpoint)

  { }

 }

}

Figure 13: Adding a message formatter using a WCF endpoint behavior

Through the technique illustrated above, we are instructing WCF to use our custom message formatter when a message is sent or received by the WCF service. 

Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Until this point, we have explored a series of WCF extensibility points that fundamentally manipulate the message representations on the client or dispatcher runtimes. After the WCF runtimes translate a message into objects by using a message formatter, there are other WCF components that interact with the objects that are ultimately processed by the client or service application. These components represent another set of WCF extensibility points which are closer to the service and client definition as they manipulate CLR specific components such as objects, parameters or operations. By leveraging these extensibility points, developers can customize key functionalities such as parameter inspection or operation selection and invocation all of which are the subject of our next section.

Parameter inspectors

Overview

WCF parameter inspectors are extensibility components that allow inspecting the parameters of a service or client call. These types of components can be seen as providing complementary functionality to the message inspectors explored in previous sections. Similar to message inspectors, parameter inspectors implement message filtering and inspection on operation parameters and result types.

WCF abstracts the concept of a parameter inspector by using the System.ServiceModel.Dispatcher.IParameterInspector interface. This interface is used for parameter inspectors executed in the client and dispatcher runtimes.

Scenarios

Parameter inspectors are one of the most general WCF components which can be applied ubiquitously across a large variety of scenarios. By implementing these components, developers can add additional levels of parameter validations and filtering in a completely orthogonal manner to the service or client functionality. The following list illustrates some of the most common functionalities that can be implemented with parameters inspectors:

  • Parameter Validation: Parameter inspectors can be used as a vehicle for injecting complex validation logic against the operation parameters and result types that complement the service and client code. For instance, a parameter inspector can validate the parameters of an operation by using Windows Workflow Foundation rules.

Implementation

The fundamental task of implementing a WCF parameter inspector is implementing the System.ServiceModel.Dispatcher.IParameterInspector interface. From the WCF’s programming model standpoint, the experience of implementing a parameter inspector is identical whether the inspector is hosted in the client or service side. The following code illustrates sample implementation of a parameter inspector.

public class SampleDispatchParameterInspector: IParameterInspector

{

 public void AfterCall(string operationName, object[] outputs,

                                 object returnValue, object correlationState)

 {

    LogParameters(outputs, returnvalue);

   …Implementation has been omitted by brevity….

  }

 public object BeforeCall(string operationName, object[] inputs)

 {

   LogParameters(innputs);

   Console.WriteLine("Custom parameter inspector:

                                            Executing operation BeforeCall");

    …Implementation has been omitted by brevity….

    return null;

 }

}

Figure 14: Custom parameter inspector

The BeforeCall and AfterCall methods are invoked by the WCF runtime to intercept the parameters before and after a client or service operation is invoked.

In order to inject the parameter inspector into the WCF client or dispatcher runtimes we need to add it to the ParameterInspectors property of the DispatchOperation object. This can be achieved in the context of the WCF behavior as illustrated in the following code:

public class CustomExtensibilityEndpointBehavior: IEndpointBehavior

{

  public void AddBindingParameters(ServiceEndpoint endpoint,

                               BindingParameterCollection bindingParameters)

  { }

  public void ApplyClientBehavior(ServiceEndpoint endpoint,

                                                ClientRuntime clientRuntime)

  { }

  public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                                       EndpointDispatcher endpointDispatcher)

  {

   SampleDispatchParameterInspector paramInspector =

                                      new SampleDispatchParameterInspector();

   foreach (DispatchOperation operation in

                               endpointDispatcher.DispatchRuntime.Operations)

     operation.ParameterInspectors.Add(paramInspector);

  }

  public void Validate(ServiceEndpoint endpoint)

  { }

}

Figure 15: Initializing custom parameter inspector

Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Operation Selector

Overview

One of the fundamental tasks of any Web Service runtime in object oriented technologies is to determine what operation to invoke on the service side based on the contents of a message. WCF implements this functionality by using operation selectors which are other extensibility points of the dispatcher runtime. Essentially, operation selectors are responsible for inspecting the message and indicating to the WCF runtime what operation to invoke in the service’s class. From a programming model standpoint, operation selectors are abstracted by the System.ServiceModel.Dispatcher.IDispatchOperationSelector interface.

 As you already know, WCF includes a set of default operation selectors that implement the most common operation selection models in the SOAP and REST styles. However, some scenarios require an extra level of customization on this functionality.

Scenarios

You can consider implementing a custom operation selector in scenarios that require a specific logic for determining the operation to invoke on the service instance that is not covered by the default selectors. The following list includes some of the most common cases for considering implementing a custom operation selector:

  • Content based selection: Certain scenarios might require inspecting the contents of the message in order to determine the service operation that needs to be invoked.
  • Custom Action Mapping: In some scenarios the dispatcher runtime might need a more sophisticated algorithm to map a SOAP or WS-Addressing action to a service operation. For instance, a custom implementation can use a simple and highly customizable rules engine to map the WS-Addressing headers to a specific operation.

Implementation

The fundamental step of building a custom operation selector consists of implementing the System.ServiceModel.Dispatcher.IDispatchOperationSelector interface and specifically the SelectOperation method. This operation outputs the name of the method that should be invoked in the service instance based on the details of the incoming message. The following figure illustrates a sample implementation of an operation selector:

public class SampleOperationSelector: IDispatchOperationSelector

{

        public string SelectOperation(ref Message message)

        {

            …Implementation has been omitted for brevity…

            return Operation’s Name;

        }

}

Figure 16: Operation selector sample implementation

We can initialize our custom operation select by setting the OperationSelector property of the dispatcher runtime. Similarly to other extensibility components, we can access the dispatcher runtime by implementing a custom behavior:

public class MsgExtensibilityEndpointBehavior: IEndpointBehavior

{

  public void AddBindingParameters(ServiceEndpoint endpoint,

                               BindingParameterCollection bindingParameters)

  { }

  public void ApplyClientBehavior(ServiceEndpoint endpoint,

                                                ClientRuntime clientRuntime)

  { }

  public void ApplyDispatchBehavior(ServiceEndpoint endpoint,

                                       EndpointDispatcher endpointDispatcher)

  {

   SampleDispatchParameterInspector paramInspector =

                                      new SampleDispatchParameterInspector();

   foreach (DispatchOperation operation in

                               endpointDispatcher.DispatchRuntime.Operations)

     endpointDispatcher.DispatchRuntime.OperationSelector =

                                               new SampleOperationSelector();

  }

  public void Validate(ServiceEndpoint endpoint)

  { }

}

Figure 17: Adding an operation selector using an endpoint behavior

Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Operation Invoker

Overview

After executing a set of the components explored in previous sections, the dispatcher runtime needs to invoke the service operation by passing it the corresponding parameters. This capability is abstracted by WCF using operation invokers which are another important component of the WCF extensibility programming model. Fundamentally, WCF models operation invokers by using the System.ServiceModel.Dispatcher.IOperationInvoker interface.  By implementing that interface, developers can gain control over how the service operation is invoked and the results (if any) are initially processed.

Scenarios

Custom operation invokers allow developers to control the process of invoking an operation in a WCF service instance. This capability enables a variety of scenarios like the ones included in the following list:

  • Dependency Injection: A custom operation selector can dynamically inject custom logic that extends the implementation of a service operation. For instance, a custom operation invoker can execute an operation of a specific interface extracted from a dependency injection container such as Microsoft's Unity right before the service operation is invoked.
  • Pre and Post operation logic: We can implement a custom operation selector to execute logic that executes right before and after a service operation is invoked. This feature could be really useful to enable capabilities such as tracking operation execution metrics or incorporating compensation logic.

Implementation

The fundamental step of implementing a WCF operation invoker is to implement the System.ServiceModel.Dispatcher.IOperationInvoker interface which abstracts the mechanisms for invoking an operation synchronously and asynchronously. The following code illustrates a sample implementation of this interface:

public class SampleOperationInvoker: IOperationInvoker

    {

        private IOperationInvoker innerOperationInvoker;

        public SampleOperationInvoker(IOperationInvoker innerinvoker)

        {

            innerOperationInvoker = innerinvoker;

        }

        public bool IsSynchronous

        {

            get { return true; }

         }

        public object Invoke(object instance, object[] inputs,

                                                 out object[] outputs)

        {

            …Implementation has been omitted for brevity…

            return innerOperationInvoker.Invoke(instance, inputs,

                                                           out outputs);

        }

        public object[] AllocateInputs()

        {

            return innerOperationInvoker.AllocateInputs();

        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs,

                                     AsyncCallback callback, object state)

        {

            …Implementation has been omitted for brevity…

            return innerOperationInvoker.InvokeBegin(instance, inputs,

                                                          callback, state);

        }

        public object InvokeEnd(object instance, out object[] outputs,

                                IAsyncResult result)

        {

            …Implementation has been omitted for brevity…

            return innerOperationInvoker.InvokeEnd(instance, out outputs,

                                                                   result);

        }

    }

Figure 18: Custom operation invoker

As illustrated in the previous code, our custom operation invoker implements the methods for calling a service’s method synchronously and asynchronously. We can initialize our operation invoker by setting the Invoker property of the operation runtime object. One of the easiest ways to accomplish that is by implementing a custom operation behavior as illustrated in the following code:

public class CustomExtensibilityOperationBehavior: Attribute,

                                                          IOperationBehavior

    {

        public void AddBindingParameters(OperationDescription

          operationDescription, BindingParameterCollection bindingParameters)

        { }

        public void ApplyClientBehavior(OperationDescription

                       operationDescription, ClientOperation clientOperation)

        {

        }

        public void ApplyDispatchBehavior(OperationDescription

                   operationDescription, DispatchOperation dispatchOperation)

        {

          …Implementation has been omitted for brevity…

          dispatchOperation.Invoker = new

                           SampleOperationInvoker(dispatchOperation.Invoker);

        }

        public void Validate(OperationDescription operationDescription)

        { }

    }

Figure 19: Instantiating a custom operation invoker

We can use the previous behavior by simply applying it as an attribute to the service operation we are trying to customize the invocation from.

Creating a custom WCF error handler

Introduction

Error handling is one of the most important aspects of distributed systems and particularly those that follow the principles of service orientation. Most of the Web Services technology stacks enable sophisticated mechanisms for mapping native exceptions to representation formats such as SOAP faults or HTTP error codes that can be transmitted over the wire. Quite often, developers need to extend or customize these mechanisms in order to support some of the Web Services error handling capabilities required by service oriented scenarios.

WCF provides a versatile programming model that transforms managed exceptions into SOAP faults and vice-versa. Using this model, developers can handle declared (part of the service contract) and undeclared operations as part of WCF solutions. Additionally, WCF’s exception handling model presents a series of extensibility points that allow developers to incorporate custom error handling mechanisms directly at the infrastructure level.

The fundamental element of this extensibility model is the System.ServiceModel.Dispatcher.IErrorHandler which allows developers to manipulate application-level exceptions and send the corresponding fault messages to the client side. Using this interface, we can implement error handling mechanisms that can be used ubiquitously across multiple services. For instance, let’s assume we are building a set of Web Services that implement a B2B standard. As part of the implementation we can incorporate different error handlers that transform the service’s exceptions into a format compliant to the B2B standard.

Scenarios

Custom error handlers are a very common extensibility point used in WCF solutions. Fundamentally, developers should consider implementing custom error handlers to extend the fundamental WCF programming model by implementing infrastructure-level exception handling mechanisms. The following list includes some of the scenarios that can be enhanced by the use of custom error handlers:

  • Standard error messages: Error standardizationis a common practice in service-oriented enterprise applications. For instance, a financial institution could standardize the specific SOAP faults formats that should be followed by the different WCF services in the enterprise. This scenario can be addressed by implementing a custom WCF error handler that transforms the generated exceptions in a message compliant by the enterprise standards.
  • Monitoring: WCF error handlers are often used to track and monitor the exceptions generated by WCF services. For instance, we can implement a custom WCF error handler that logs service’s exceptions to a custom database in order to enable capabilities such as error reporting and analysis.

Implementation

WCF abstracts error handlers by using the System.ServiceModel.Dispatcher.IErrorHandler interface. Developers can implement this interface in order to implement custom error handling mechanisms. Specifically, the WCF runtime will call the ProvideFault method of a custom event handler in order to build the fault message that should be sent to the client applications. Additionally, custom event handlers should implement the HandleError operation to ensure error-related behaviors, including error logging, shutting down the application, etc. The following code illustrates the implementation of a custom error handler:

public class SampleErrorHandler : IErrorHandler

{

public bool HandleError(Exception error)

{

  ...Implementation has been omitted for brevity...

  return true;

}

public void ProvideFault(

  Exception error,

  MessageVersion ver,

  ref Message msg

)

{

  FaultException<SampleFault> fe

    = new FaultException<GreetingFault>(new SampleFault(error.Message));

  MessageFault fault = fe.CreateMessageFault();

  msg = Message.CreateMessage(

    ver,

    fault,

    "http://tellago.servicemodel.com/ISampleService/SampleMethodFault"

  );

}

}

Figure 20: Custom error handler

When an error occurs, the WCF runtime will invoke the error handlers included in the error handler’s collection of the System.ServiceModel.Dispatcher.ChannelDispatcher object. We can add our custom error handler to that collection by implementing a custom behavior as illustrated in the following code:

public class SampleErrorBehavior : IServiceBehavior

{

    void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)

    {

    }

    void IServiceBehavior.AddBindingParameters(

                                       ServiceDescription description,

                                       ServiceHostBase serviceHostBase,

                                       Collection<ServiceEndpoint> endpoints,

                                       BindingParameterCollection parameters)

    {

    }

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription

                                description, ServiceHostBase serviceHostBase)

    {

        SampleErrorHandler errorHandler = new SampleErrorHandler();

        foreach (ChannelDispatcherBase channelDispatcherBase in

                                          serviceHostBase.ChannelDispatchers)

        {

            ChannelDispatcher channelDispatcher = channelDispatcherBase as

                                                          ChannelDispatcher;

            channelDispatcher.ErrorHandlers.Add(errorHandler);

        }

    }

    ...The rest of the implementation has been omitted for brevity...

}

Figure 21: Initializing a custom event handler using a service behavior

As illustrated in the previous code, the custom service behavior creates an instance of our custom error handler and adds it to the channel dispatcher so that it can be used by the WCF runtime. Similar techniques can be implemented with endpoint behaviors.

Instance provider

Overview

One of the great capabilities of WCF is the flexible model that controls how services are instantiated in response to messages processed by the dispatcher runtime. In its current version, WCF includes the default instancing models listed below:

  • PerCall: A new service object is created for each client request.
  • PerSession: A new service object is created for each new client session and maintained for the lifetime of that session (this requires a binding that supports sessions).
  • Single: A single service object handles all client requests for the lifetime of the application.

Internally, WCF delegates the creation of a service instance onto a component known as instance providers. These components are abstracted by the System.ServiceModel.Dispatcher.IInstanceProvider interface and are responsible for controlling the service’s instances in the WCF’s dispatcher runtime. Developers can build custom instance providers in order to implement additional instancing patterns that can be used by WCF services.

Scenarios

Custom instance providers are frequently used in scenarios in which the number of service instances that can be created is determined by certain constraints. The following are some of the most common scenarios for implementing a custom instance provider.

  • Instance pooling: In this scenario, the dispatcher runtime can only run a specific number of concurrent service instances. In order to address this scenario and minimize the service creation overhead we can implement a custom instance provider that keeps a pool of service instances which are passed to the WCF runtime using a specific distribution algorithm. In that sense, service instances are placed in a pool as created. Once the maximum number of concurrent instances is reached, the runtime will dispatch subsequent requests to one of the instances in the pool.
  • Custom initialization: By implementing a custom instance provider developers can initialize a service with a custom state based on the incoming message.

Implementation

WCF instance providers are abstracted by the System.ServiceModel.Dispatcher.IInstanceProvider interface which includes the required methods for creating new service instances. Specifically, the overloaded versions of the GetInstance method are responsible for providing the dispatcher runtime with the service instance that should be used to process the incoming request. In a similar manner, the ReleaseInstance method is invoked by the runtime in order to release the resources associated with a specific service instance. The following code illustrates a sample instance provider:

public class SamplePoolInstanceProvider: IInstanceProvider

    {

        Type currentType

        public SamplePoolInstanceProvider(Type servicetype)

        {

            currentType = servicetype;

        }

        public object GetInstance(InstanceContext instanceContext)

        {

            …Implementation has been omitted for brevity…

            return service instance;

        }

        public object GetInstance(InstanceContext instanceContext,

                                                           Message message)

        {

            …Implementation has been omitted for brevity…

            return service instance;

        }

        public void ReleaseInstance(InstanceContext instanceContext,

                                                           object instance)

        {

            …Implementation has been omitted for brevity…

        }

    }

Figure 22: Custom instance provider

This instance provider can be enabled by implementing a custom behavior as illustrated in the following code:

public class SampleInstanceProviderBehavior: IServiceBehavior

  {

      private Type serviceType;

      public SampleInstanceProviderBehavior()

      {

      }

      public void AddBindingParameters(

                      ServiceDescription serviceDescription,

                      ServiceHostBase serviceHostBase,

                      Collection<ServiceEndpoint> endpoints,

                      BindingParameterCollection bindingParameters)

      { }

      public void ApplyDispatchBehavior(

                     ServiceDescription serviceDescription,

                     ServiceHostBase serviceHostBase)

      {

         serviceType= serviceDescription.ServiceType;

         SamplePoolInstanceProvider provider=

                        new SamplePoolInstanceProvider(serviceType);

         foreach(ChannelDispatcher channelDispatcher in

                                serviceHostBase.ChannelDispatchers)

           foreach (EndpointDispatcher endpointDispatcher in

                                       channelDispatcher.Endpoints)

            endpointDispatcher.DispatchRuntime.InstanceProvider= provider;

      }

      public void Validate(ServiceDescription serviceDescription,

                                         ServiceHostBase serviceHostBase)

      {

      }

  }

Figure 23: Instantiating a custom instance provider

Following the same techniques demonstrated in this paper, we can deploy our sample behavior either programmatically or declaratively by implementing a custom behavior extension element.

Summary

This chapter has explained about a dozen of the fundamental extensibility points of WCF’s client and dispatcher runtimes. Developers can use the code samples and techniques illustrated in this chapter to extend essential capabilities of WCF’s client and dispatcher runtimes such as message encoding, filter, inspection, operation selection, instancing, among many others. Additionally, we’ve provided examples of real world scenarios that can directly benefit from the extensibility model of WCF’s client and dispatcher runtimes.

 

About the Authors

Jesus Rodriguez: Jesus Rodriguez is the Chief Architect of Tellago, Inc. He is also a Microsoft BizTalk Server MVP, an Oracle ACE and one of a few Architects worldwide to be a member of the Microsoft Connected Systems Advisor team. As a member, Jesus has been selected to participate in a variety of Software Design Reviews with Microsoft's Product Teams including Windows Communication Foundation, Windows Workflow Foundation and BizTalk Server.

Jesus derived his extensive experience with business process integration and messaging through numerous implementations of disparate systems founded on the principles of SOA and BPM. Jesus is an active contributor to the .NET and J2EE communities and an internationally recognized speaker and author with contributions that include several articles for various publications including MSDN Magazine, Microsoft Architecture Journal, SOAWorld and Web Services Journal as well as speaking engagements at top industry conferences such as Microsoft TechEd, SOAWorld, Microsoft SOA and BPM Conference, Oracle Open World, software Architect Conference, Web Services Security Conference and the Microsoft MVP Summit. Additionally, Jesus has conducted a number of Web Casts on varying SOA technologies.

Jesus is a prolific blogger on all subjects related to integration and has a true passion for technology. You can gain valuable insight on leading edge technologies through his blog at https://weblogs.asp.net/gsusx.

Pablo Cibraro: Pablo is a senior architect in Tellago Inc, and an internationally reconigzed expert with over 10 years of experience in architecting and implementing large distributed systems with Microsoft Technnologies.

He has spent the past few years working directly with the Microsoft Patterns & Practices team on sample applications, patterns and guidance for building service-oriented applications with Web Services, Web Services Enhacements (WSE) and Windows Communication Foundation (WCF). The biggest contributions with this team were the Web Services Security patterns, and the implementation of a Secure Token Service quickstart for WSE 3.0 and WCF.

He now focuses on technologies that enable developers to build large scale systems such as WCF, WF, Dublin, OSLO and Windows Azure.

The community knows Pablo mainly for his weblog and active participation in conferences and forums.