Understanding Windows Communication Foundation Extensibility

Summary: Provides a detail about the extensibility mechanisms existing in WCF. This paper covers each extensibility point in detail and, more importantly, when to use which one. This article assumes basic knowledge of WCF and it’s concepts.

Windows® Communication Foundation (WCF) is the new unified communication programming model for building service-oriented applications. It’s one of the three pillars of the NET 3.0 platform that’s part of the new Windows® Vista operating system, and it also runs on Windows® XP and Windows® 2003.

The relevance of this new technology is that it unify all previous methods for building distributed systems: ASMX, Remoting, Enterprise Services (COM+), WSE (Web Service Enhancements) and MSMQ (Message Queuing), and all that diversity may certainly sound good but in fact imposes knowing very different programming models to developers and choosing one over another can be hard and costly.

Developing this kind of technology is not easy and many people at Microsoft devoted many years of their lives in it’s creation, solving very complex problems and designing a unique architecture where everything has the right level of abstraction, flexibility and extensibility.

WCF has three main aspects that define it’s design: the unification of several existing Microsoft communication technologies, support for cross-vendor interoperability, and support for explicit service-orientation, but extensibility is a very important implementation detail that could make developer’s live easier and could push the adoption of this new technology.

A little of history

The extensibility mechanisms found in WCF have their roots in Remoting and the later WSE but meanwhile you might recognize a few similar concepts, digging a little deeper in the implementation details reveal a richer model with much more specialized constructs that at first look may seem too complex. This apparent complexity is pretty much the feedback I received from anybody trying to implement or migrate some extension in WCF, and after understanding the model is clear that the added complexity pays off in clarity and ease for the developers, and that’s the main reason why I’m writing this paper.

In the first .NET runtime we basically had two native communication technologies: Web Services (by means of ASP.NET) and Remoting. In the previous paragraph I say “native” because we also had the COM+ (Enterprise Services) integration story but I don’t consider this to be part of a common communication scenario within an application, or even a SOA model integrating different applications or systems because is tied to an specific technology.

In ASP.NET Web Services we had (and still have) extensibility through the use of the very nice HttpHandler and HttpModule classes. ASP.NET has a pipeline architecture where you can customize the behavior of the pipeline by adding or removing standard or custom handlers and modules.

This pipeline architecture has proven to be very flexible and useful, so it’s no surprise to see it used in other (old and new) Windows® components, and Remoting is no exception.

Remoting was designed with extensibility in mind so you can customize any of the stages found in the Remoting pipeline:

  1. Proxy: transform method calls into Remoting messages.
  2. Formatter: serializes messages into byte arrays.
  3. Custom Sinks: custom process for messages, like security, compression, tracing and many more.
  4. Transport: perform the delivery of the information using a transport like Http, Tcp, Udp, Smtp, etc.

Although Remoting has an extensible design and after some time we have at our hand several very useful extension libraries produced my third parties, the community and Microsoft itself, this technology has a strong focus on RPC style of communication and is strongly tied to the .NET platform making it unsuitable for being the universal communication technology.

Having said that I want to make perfectly clear that Remoting is still a useful technology and has a very important place in the .NET platform. As Microsoft has stated several times, Remoting is not going anywhere any time soon.

So, after some time it was clear that a better solution for application communication is needed, and then WSE (Web Service Enhancements) was born. WSE has a clear focus on Web Services (or SOAP Services) and some or its goals were:

  • Implement a SOAP Service stack that is protocol independent.
  • Give the ASP.NET Web Services developers a more flexible and modern stack to work with.
  • Give the ASP.NET Web Services developers support the newer WS-* standards.

WSE was also a playground for testing new architectures and designs towards a new and unified communication model that is now know as WCF. WSE developers were told that WCF migration and interoperation would be assured at some degree, and although the implementation details of WCF and WSE are very different all the basic components are still there and is very easy to correlate with each other.

The WSE stack has a very nice architecture, extensible and flexible and in some aspects reminds you to Remoting or WSE. The first big change compared to ASP.NET Web Services is that you have protocol independence so you are no longer tied to ASP.NET hosting in IIS and Http, but you also find some familiar names like proxies, dispatchers, formatters, transports and channels.

What we have in WCF

And if you have the feeling that WCF is an evolution over all previous communication technologies we talked about, you are right on the spot.

WCF has a design that clearly shows a very hard work of many talented people for a long time, and that kind of effort can only be a good thing.

The first thing you note when start working with WCF is the simplicity of the programming model, a thing that COM+ and Enterprise Services was very good atproviding some interesting programming models, like declarative programming using attributes, that WCF tried to incorporate.

After that first impression you start digging a little more into the WCF inner workings and you note that it has all the flexibility and extensibility of WSE and Remoting, and then some more, and also supports the communication style of Remoting (although the WCF team doesn’t want to encourage its use) side by side with the full Service Orientation support.

As WCF really is the unification of all previous communication technologies and development paradigms for application and integration scenarios, we can expect to find a more complex architecture underneath, and as I said before it can be a little intimidating when you start looking into all those interfaces and options for extensibility.

Figure 1. WCF Architecture

As we can see in the simplified architecture diagram shown above, WCF has two main layers where extensibility can be applied and the first decision you have to make is at what layer you need to apply the extensibility you need:

  1. Customizations at the service model level: this is the higher level of abstraction in WCF and they apply to the higher level constructs like service, contracts and operations. Most common customizations are made at this level and they relate to the fact that you can change the local behavior of any of those service constructs.
  2. Customizations at the messaging level: this is the lower level of the WCF infrastructure where you have protocols and transport channels. In this layer the message is serialized and transmitted using the selected transport, also protocol rules are applied (like reliable messaging) and security (like encryption). Customizations here are needed when you need to modify “what’s on the wire”.

Other extensibility points beyond these two include Bindings, Security, Metadata, Serializers and Hosting but they are somewhat less common in typical applications.

The three ways of customizing the service layer include:

  • Manipulate the Service Description.
  • Injecting Behaviors by hand.
  • Overriding ServiceHost / ChannelFactory<T>.

We are going to dive into these three customization methods in the next sections.

Service Description

Figure 2. ServiceDescription at runtime

The Service Description is the heart of the service and is created when a ServiceHost or client ChannelFactory is created to describe all aspects of the service, endpoints and behaviors. This description is a logical view of what the WCF runtime is going to build when the Open method on the ServiceHostis executed, and also is used to generate the WSDL and policy metadata for the service. This metadata is exposed through the ServiceDescription class and the Description property on the ServiceHost and can be changed, for example, by injecting behaviors. To build the ServiceDescription information the WCF runtime first reflects over the service types and then it loads the service configuration. This information is then used when you execute the Open method of the ServiceHost to build the runtime.

In the code shown below you can see how the service description information could be accessed and modified:

// Construct the ServiceHost

ServiceHost host = new ServiceHost(typeof(EchoService), new Uri(“ );

// Add endpoints to description

host.AddServiceEndpoint(typeof(IEchoService), new BasicHttpBinding(), “”);

// You can access the Description property of the host

Console.Writeline(host.Description.ServiceType);

// And you can change things before calling Open

foreach( ServiceEndpoint endpoint in host.Description.Endpoints )

{

// Add My behavior to all Http endpoints

If( endpoint.Uri.Scheme == “http” )

endpoint.Behaviors.Add( new MyHttpBehavior() );

}

// Open the Host

Host.Open();

Building a custom ServiceHost

The general WCF application will not be using a custom service host, but there are scenarios where this could be necessary. In an application hosted in IIS the host is created automatically for you but a service host class can be specified in the .svc file.

In cases where a service is hosted in a WinForms application or Windows Service and you need to implement things like callbacks or notifications.

Writing a custom service host class is a simple procedure, and in order to do that you must:

  • Create a class that derives from ServiceHost.
  • Override the “CreateDescription” method.
  • Override the “ApplyConfiguration” method.
  • Override the “InitializeRuntime” method.

Each of those methods corresponds to each of the steps described in the figure 2, where a service description is created by reflecting the service types, then a configuration information is applied to that and after that, when you call the “Open” method, the runtime is initialized.

Creating a custom service host class could be useful for automating repetitive code like setting properties or verifying that a specific service configuration is in place, for example, that certain protocols or behavior are correctly configured.

Steps to extend the Service Model layer

Implementing each type of extensibility needs a three step process:

  1. Identify what extensibility point you need to use:
  2. What do you want to modify or inspect? Typically this can be a message (using IClientMessageInspector or IDispatchMessageInspector) or parameter (using IParameterInspector). Another option here is to implement a custom message formatting (using IClientMessageFormatter or IDispatchMessageFormatter).
  3. Where you are going to use it: depending on the scope you want to affect with the above interceptors, and the options are: Service (affects the whole service using an IServiceBehavior), Contract (affects a specific contract using an IContractBehavior), Operation (affects only one operation from a contract using an IOperationBehavior) and Endpoint (affects an specific endpoint using an IEndpointBehavior).
  4. Build your extension. This generally involves coding the interceptors (message or parameter inspector or formatter) and a behavior for applying this changes to the service code. If you want to support applying this behavior using attributes you must code a simple custom attribute class, and if you want to support adding this behavior using configuration files you must implement at least one custom configuration element.
  5. Plug in your extension, using some of the methods described below.

To plug in a extension into the WCF infrastructure you have four options, described below:

  1. Implement a behavior or a binding/bindingelement.
  2. Insert programmatically your behavior or binding/binding element.
  3. Insert your behavior using a custom attribute.
  4. Insert your behavior or binding/bindingelement using a configuration file.

In WCFextensibility can be done through the use of behaviors and inspectors. Together they allow you to customize every aspect of the way WCF works.

Behaviors

A behavior allows you to change the way WCF functions, but they are not doing the actual work. Instead of that they configure some part of WCF (a operation, a contract or endpoint) by injectinginterceptors (message, operation or parameter inspectors).

Most of the time I think of behaviors as configuration helpers and they are very powerful when implemented as attributes that can be applied declaratively to different parts of your service code.

Figure 3. Behaviors in action

The interceptors do the actual work and they are similar in function to Remoting custom sinks or WSE filters. The importance of behaviors is that you can create one that configures a number of different points in the WCF pipeline in a controlled and known way. For example, you can create an “UltraSecureService” behavior where you add several interceptors to configure message and protocol encryption, reliable messaging and authentication, all at once.

Behaviors can be applied to any extensibility point in WCF by using attributes, programmatically (Description.Behaviors.Add in the ServiceHost) or by configuration. All added behaviors appear in the ServiceDescription information for the service.

There are four kind of behaviors in WCF:

  • Service behaviors (IServiceBehavior types) enable the customization of the entire service runtime including ServiceHostBase.
  • Endpoint behaviors (IEndpointBehavior types) enable the customization of service endpoints and their associated EndpointDispatcher objects.
  • Contract behaviors (IContractBehavior types) enable the customization of both the ClientRuntime and DispatchRuntime classes in client and service applications, respectively.
  • Operation behaviors (IOperationBehavior types) enable the customization of the ClientOperation and DispatchOperation classes, again, on the client and service.

All these four different behaviors are essentially the same but they provide you with a different contextual information at runtime in order to inject the proper interceptors. The main reason for this different behaviors to exists is the different scopes in the WCF runtime you want to affect with the interceptor you are about to inject.

All behaviors provide the following mechanisms for doing they work:

  • Validation: the “Validate” method is called just before the runtime is built and allows you to perform validation tasks over the service description, for example checking for some specific configuration you expect. WCF has several behaviors that allows you to define service requirements that are validated at runtime, for example, examining the service bindings and checking for session or reliability compliance.
  • Binding parameter customization: the “AddBindingParameters” method is called in the first stage of the runtime building (before underlying channel is constructed) and allows you to add or change parameters that affect the building of that underlying channel.
  • Applying the behavior: the method “ApplyClientBehavior” or “ApplyDispatchBehavior” is called to apply the current behavior.

Interceptors

These are the places where you actually change or extend the processing pipeline of WCF by altering messages or other objects as they enter or exit the infrastructure. There are several different interceptors that can be attached to different points and they allow to alter the behavior of any part in the WCF pipeline.

Some of the different type of interceptors that we can use are:

  • Message inspectors: in the form of client and server message interceptors for altering or inspecting message contents.
  • Parameter inspectors: the same as before but applied to parameters in service operations.
  • Message formatters: allows you to change the format of the message, for example for supporting other platforms or formatting rules.

Sharing State between interceptors

A common scenario in WCF is where you have added to the pipeline several interceptors that need to share some state, and in order to do that you must use WCF Extensions.

Extensions are available at different scopes:

  • ServiceHost
  • InstanceContext
  • OperationContext

And they are always available through the “Extensions” property by means of an “IExtensionCollection<T>” interfase.

The Service Model Level

At this level you can extend, customize or replace any component that affects the service local behavior. When I say local behavior I mean at the client or server side, but both sides are totally independent and there are several kinds of extensibility that doesn’t need an symmetric implementation (for example, logging or tracing).

The two most important classes here are the Proxy and the Dispatcher, so extensibility at this level refers to methods to change the behavior of those two objects.

Client Extensibility

The service model layer is the main piece of WCF allowing the transformation of application process and objects into messages being exchanged. Extensions at this level allows to modify the behavior or features found in the client proxy through message and parameter interception.

Figure 4. Client extensibility points

When an application calls an operation, the ClientOperationclass translates the call and outbound parameters into a message, processes interceptors, confirms that the outbound call conforms to the target contract, and passes the outbound message to the ClientRuntime class, which is responsible for creating and managing the needed channels, handling extra outbound message processing (such as header modification), processing message interceptors in both directions, and routing inbound duplex calls to the appropriate client-side DuplexRuntime object. Both the ClientOperation and ClientRuntimeclasses provide similar services when messages (including faults) are returned to the client.