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 last of the behavior interfaces in WCF is the IOperationBehavior. The scope of this one is limited to an individual operation, so one can have different operations in the same contract. For example, the OperationBehaviorAttribute can define that some specific operations in the contract will auto-complete transactions (if the operation returns successfully, i.e., does not throw an exception). Operation behaviors are typically used for those scenarios, when one wants fine-grained control over the operations in a service contract.
Again, like in the previous behavior interfaces, Validate is called first, with the operation description passed as a parameter. If the behavior finds the description violating some of its logic, it should throw an exception to abort the host (or the client) from opening. AddBindingParameters is called next, and again this is not commonly used.
ApplyDispatchBehavior and ApplyClientBehavior are called last, and they receive the operation description and the operation runtime (DispatchOperation for the server side, ClientOperation for client side). Among the properties in the operation runtime which are commonly used are the list of parameter inspectors (client / server) and the message formatter (client / server), which will be covered later in this series.
Via service / endpoint / contract description (server): You can obtain a reference to the operation description via the contract description, and from there you can get a reference to the list of operation behaviors:
Via channel factory / generated proxy (client): Like on the server side, once you get a reference to the endpoint, you can reference the contract, then the operation description collection. Notice that (just like in the service) you don’t need to enumerate all the operations, you can select by name to apply the behavior to specific operations.
Via attributes: If the operation behavior is derived from System.Attribute, it can be applied to the operation, either at the contract interface or at the actual implementation of the operation in the service code, and it will be added to the operation description by WCF. Adding it to both places is the same as adding it to a single one (the list of behaviors is a dictionary keyed by the behavior type, so a behavior collection cannot have more than one instance of each type).
Using configuration: Configuration is not an option for adding operation behaviors.
The IParameterInspector interface is a great way to analyze the inputs and outputs to operations in WCF services – it provides a callback where one can look, log, change, etc. the input and output parameters. It also lets the users see the return value of the operation, but doesn’t let the inspector change it. A few posts from the forums needed to actually change the return value as well. In one of them, the user Antonello Tesoro offered a great, simple suggestion on how to implement such inspector (wrapping an operation invoker), and I’ll implement his idea to show a real usage of operation behaviors.
And here goes the usual disclaimer – this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, for simplicity sake it doesn’t have a lot of error handling which a production-level code would, and doesn’t support all contract types (asynchronous operations, for example, are only partially supported). Finally, this sample (as well as most other samples in this series) uses extensibility points other than the one for this post (e.g., operation invoker) which are necessary to get a realistic scenario going. I’ll briefly describe what they do, and leave to their specific entries a more detailed description of their behavior.
First, let’s define the new inspector interface. It’s almost a copy of IParameterInspector, with the only difference being the returnValue parameter to AfterCall being passed as reference:
On to the behavior. On the call to ApplyDispatchBehavior, the DispatchOperation object is already created and populated. We’ll then replace the existing operation invoker with our own implementation, passing the existing one to it:
The new operation invoker will simply delegate all the calls to the original invoker, calling the inspector before and after completing each call. On synchronous method implementations, the implementation of the correlation state is trivial – simply store the return value of BeforeCall in a local variable, invoke the operation, then use that variable as the correlationState parameter to AfterCall. On asynchronous operations, however, it isn’t as simple. For this sample, the code throws if an inspector to an asynchronous operation returns anything other than null on BeforeCall. To fully implement correlation state for such operations, we’d need to chain the asynchronous operation as described in http://blogs.msdn.com/b/carlosfigueira/archive/2009/07/23/chaining-async-calls-in-wcf-or-in-net-in-general.aspx – too complex for this sample.
And that’s it. Now let’s put them to use. To test this scenario, I’ll define two service contract interfaces, one with synchronous operations, one with asynchronous ones.
Their implementations are trivial (for the asynchronous one we delegate to the Func<T,TResult> delegate and let it handle the asynchronous behavior for us).
Now for setting up the service. This operation behavior needs an instance of an inspector, so it cannot be defined as an attribute (attribute arguments cannot be user-defined objects). We’ll define two inspectors, one for the calculator (no negative numbers) and one for the asynchronous operations (no null return values):
Now for setting up the service itself (and calling it). In one of the endpoints the behavior is applied to all operations; in the other, the behavior is applied to one operation explicitly.
That is it. This is an example on how an operation behavior can be used to implement this scenario. Notice that it only works for the server side, as there’s no invoker at the client side (for that side I think I’d use an IClientMessageFormatter). If there is enough demand I’ll expand that scenario in the upcoming post for that interface.
We’ll look at the interfaces for the runtime, starting at the inspection interfaces (IClientMessageInspector / IDispatchMessageInspector / IParameterInspector).
[Code for this post]
[Back to the index]