WCF Extensibility – Behaviors

WCF Extensibility – Behaviors

This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.

The first part of this series will focus on the behaviors. There are four kinds of behaviors, depending on the scope to which they apply: service, endpoint, contract and operation behaviors. The behavior interfaces are the main entry points for almost all the other extensibility points in WCF – it’s via the Apply[Client/Dispatch]Behavior method in the behavior interfaces where a user can get a reference to most of them.

Description vs. Runtime

The WCF behaviors are part of a service (or endpoint / contract / operation) description – as opposed to the service runtime. The description of a service are all the objects that, well, describe what the service will be when it starts running – which happens when the host for the service is opened (e.g., in a self-hosted service, when the program calls ServiceHost.Open). When the service host instance is created, and endpoints are being added, no listeners (TCP sockets, HTTP listeners) have been started, and the program can modify the service description to define how it will behave once it’s running. That’s because, during the service setup, there are cases where the service is in an invalid state (for example, right after the ServiceHost instance is created, no endpoints are defined), so it doesn’t make sense for the host to be started at that point.

When Open is called on the host (or, in the case of an IIS/WAS-hosted service, when the first message arrives to activate the service), it initializes all the appropriate listeners, dispatchers, filters, channels, hooks, etc. that will cause an incoming message to be directed to the appropriate operation. Most of the extensibility points in WCF are actually part of the service runtime, and since the runtime is not initialized until the host is opened, the user needs a callback to notify it that the runtime is ready, and the hooks can be used.

The order in which the methods of the interfaces are called is the following:

Validate: This gives the behavior an opportunity to prevent the host (or the client) from opening (by throwing an exception) if the validation logic finds something in the service / endpoint / contract / operation description which it deems invalid.

AddBindingParameters: This gives the behavior an opportunity to add parameters to the BindingParameterCollection, which is used by the binding elements when they’re creating the listeners / factories at runtime. Useful to add correlation objects between behaviors and bindings. For service behaviors, this is actually called once per endpoint, since the BindingParameterCollection is used when creating the listeners for each endpoint.

Apply[Client/Dispatch]Behavior: This is where we can get reference to the runtime objects, and modify them. This is the most used of the behavior methods (in most cases the other two methods are left blank). On the posts about each specific behavior I’ll have examples of them being used in real scenarios.

Among each method, first the service behavior is called, then the contract, then the endpoint, and finally the operation behaviors within that contract.

Adding behaviors to WCF

The behaviors can be added in three different ways:

Code: The description of the service / endpoint / contract / operation objects have a property with a collection of behaviors associated with that object; by using this reference you can simply add one of the behaviors.

Attributes: An easy way to add behaviors is by using attributes – except endpoint behaviors. By creating an attribute class (base type == System.Attribute) and implementing one of the behavior interfaces, you can simply apply the attribute to the appropriate element, and the behavior is added to the element’s description. The code below shows a simple service, with attribute-based behaviors added which print the order in which they’re called (plus an endpoint behavior added via code).

I don’t think there’s any “preferred” way of adding behaviors, it depends on the scenario. For example, if one wants to let an admin configure the behaviors without recompiling, configuration extensions are a good option; choosing between attribute and code is a choice between declarative and imperative implementation (I won’t go into that quasi-religious discussion here :)

Coming up

Posts for each of the specific behavior interfaces, in which I’ll try to find real questions posted in the forum to show how those behaviors can be used to solve a realistic scenario.