Dispatchers are 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. Dispatcher extensions allow you to modify this processing. You can implement message or parameter inspectors that inspect or modify the contents of messages or parameters. You can change the way messages are routed to operations or provide some other functionality.
This topic describes how to use the DispatchRuntime and DispatchOperation classes in a Windows Communication Foundation (WCF) service application to modify the default execution behavior of a dispatcher or to intercept or modify messages, parameters, or return values prior to or subsequent to sending or retrieving them from the channel layer. For more information about the equivalent client runtime message processing, see Extending Clients. To understand the role that IExtensibleObject types play in accessing shared state between various runtime customization objects, see Extensible Objects.
The service model layer performs the conversion between the developer’s programming model and the underlying message exchange, commonly called the channel layer. In WCF the channel and endpoint dispatchers (ChannelDispatcher and EndpointDispatcher, respectively) are the service components responsible for accepting new channels, receiving messages, operation dispatch and invocation, and response processing. Dispatcher objects are receiver objects, but callback contract implementations in duplex services also expose their dispatcher objects for inspection, modification, or extension.
The channel dispatcher (and companion IChannelListener) pulls messages out of the underling channel and passes the messages to their respective endpoint dispatchers. Each endpoint dispatcher has a DispatchRuntime that routes messages to the appropriate DispatchOperation, which is responsible for calling the method that implements the operation. Various optional and required extension classes are invoked along the way. This topic explains how these pieces fit together, and how you might modify properties and plug your own code in to extend the base functionality.
Dispatcher properties and modified customization objects are inserted by using service, endpoint, contract, or operation behavior objects. This topic does not describe how to use behaviors. For more information about the types used to insert dispatcher modifications, see Configuring and Extending the Runtime with Behaviors.
The following graphic provides a high-level view of the architectural items in a service.
A ChannelDispatcher object is created to associate an IChannelListener at a particular URI (called a listen URI) with an instance of a service. Each ServiceHost object can have many ChannelDispatcher objects, each associated with only one listener and listen URI. When a message arrives, the ChannelDispatcher queries each of the associated EndpointDispatcher objects whether the endpoint can accept the message, and passes the message to the one that can.
All properties that control the lifetime and behavior of a channel session are available for inspection or modification on the ChannelDispatcher object. These include custom channel initializers, the channel listener, the host, the associated InstanceContext, and so on.
The EndpointDispatcher object is responsible for processing messages from a ChannelDispatcher when the destination address of a message matches the AddressFilter and the message action matches the ContractFilter property. If two EndpointDispatcher objects can accept a message, the FilterPriority property value determines the higher priority endpoint.
Use the EndpointDispatcher to acquire the two main service model extension points – the DispatchRuntime and DispatchOperation classes – that you can use to customize the processing of the dispatcher. The DispatchRuntime class allows users to intercept and extend the dispatcher at the contract scope (that is, for all messages in a contract). The DispatchOperation class allows users to intercept and extend the dispatcher at an operation scope (that is, for all messages in an operation).
There a number of reasons to extend the dispatcher:
Custom Message Validation. Users can enforce that a message is valid for a certain schema. This can be done by implementing the message interceptor interfaces. For an example, see Message Inspectors.
Custom Message Logging. Users can inspect and log some set of application messages that flow through an endpoint. This can also be accomplished with the message interceptor interfaces.
Custom Message Transformations. Users can apply certain transformations to the message in the runtime (for example, for versioning). This can be accomplished, again, with the message interceptor interfaces.
Custom Data Model. Users can have a data serialization model other than those supported by default in WCF (namely, System.Runtime.Serialization.DataContractSerializer, System.Xml.Serialization.XmlSerializer, and raw messages). This can be done by implement the message formatter interfaces. For an example, see Operation Formatter and Operation Selector.
Custom Parameter Validation. Users can enforce that typed parameters are valid (as opposed to XML). This can be done using the parameter inspector interfaces. For an example, see Parameter Filter.
Custom Operation Dispatching. Users can implement dispatching on something other than action – for example, on the body element, or on a custom message property. This can be done using the IDispatchOperationSelector interface. For an example, see Operation Formatter and Operation Selector.
Object Pooling. Users can pool instances rather than allocating a new one for every call. This can be implemented using the instance provider interfaces. For an example, see Pooling.
Instance Leasing. Users can implement a leasing pattern for instance lifetime, similar to that of .NET Framework Remoting. This can be done using the instance context lifetime interfaces.
Custom Error Handling. Users can control how both local errors are processed and how faults are communicated back to clients. This can be implemented using the IErrorHandler interfaces.
Custom Authorization Behaviors. Users can implement custom access control by extending the Contract or Operation run-time pieces and adding security checks based upon the tokens present in the message. This can be accomplished using either the message interceptor or parameter interceptor interfaces. For examples, see Security Extensibility Samples.
Caution: Because altering security properties has the potential to compromise the security of WCF applications, it is strongly recommended that you undertake security-related modifications with care and test thoroughly prior to deployment.
Custom WCF Runtime Validators. You can install custom validators that examine services, contracts, and bindings to enforce enterprise-level policies with respect to WCF applications. (For example, see How to: Lock Down Endpoints in the Enterprise.)
Using the DispatchRuntime Class
Use the DispatchRuntime class either to modify the default behavior of a service or individual endpoint, or to insert objects that implement custom modifications to one or both of the following service processes (or client processes in the case of a duplex client):
The transformation of incoming messages into objects and releasing those objects as method invocations on a service object.
The transformation of objects received from the response to a service operation invocation into outbound messages.
The DispatchRuntime enables you to intercept and extend the channel or endpoint dispatcher for all messages across a particular contract, even when a message is not recognized. When a message arrives that does not match any declared in the contract it is dispatched to the operation returned by the UnhandledDispatchOperation property. To intercept or extend across all messages for a particular operation, see the DispatchOperation class.
There are four main areas of dispatcher extensibility exposed by the DispatchRuntime class:
Channel components use the properties of the DispatchRuntime and those of the associated channel dispatcher returned by the ChannelDispatcher property to customize how the channel dispatcher accepts and closes channels. This category includes the ChannelInitializers and InputSessionShutdownHandlers properties.
Instance components customize the creation, lifetime, and disposal of instances of the service type. For more information about service object lifetimes, see the InstanceContextMode property. This category includes the InstanceContextInitializers and the InstanceProvider properties.
Security-related components can use the following properties:
SecurityAuditLogLocation indicates where audit events are written.
ImpersonateCallerForAllOperations controls whether the service attempts to impersonate using the credentials provided by the incoming message.
MessageAuthenticationAuditLevel controls whether successful message authentication events are written to the event log specified by SecurityAuditLogLocation.
ServiceAuthorizationAuditLevel specifies how the auditing of authorization events is performed.
SuppressAuditFailure specifies whether to suppress non-critical exceptions that occur during the logging process.
Typically, custom extension objects are assigned to a DispatchRuntime property or inserted into a collection by a service behavior (an object that implements IServiceBehavior), a contract behavior (an object that implements IContractBehavior), or an endpoint behavior (an object that implements IEndpointBehavior). Then the installing behavior object is added to the appropriate collection of behaviors either programmatically or by implementing a custom BehaviorExtensionElement object to enable the behavior to be inserted using an application configuration file.
Duplex clients (clients that implement a callback contract specified by a duplex service) also have a DispatchRuntime object that can be accessed using the CallbackDispatchRuntime property.
Using the DispatchOperation Class
The DispatchOperation class is the location for run-time modifications and the insertion point for custom extensions that are scoped to only one service operation. (To modify service run-time behavior for all messages in a contract, use the DispatchRuntime class.)
Install DispatchOperation modifications using a custom service behavior object.
Use the Operations property to locate the DispatchOperation object that represents a particular service operation.
The following properties control runtime execution at the operation level:
The Impersonation property specifies the operation impersonation level.
The CallContextInitializers property inserts custom call context extensions for the operation.
The AutoDisposeParameters property controls when parameter objects are destroyed.
The Invoker property to insert a custom invoker object.
The ParameterInspectors property enables you to insert a custom parameter inspector that you can use to inspect or modify parameters and return values.