I mentioned earlier on that I was really enjoying playing with the extensibility hooks in Windows Communication Foundation. One of the hooks I've been playing with lately is Parameter Inspectors. A lot of the extension interfaces in windows Communication Foundation are designed to be used both from the client side as well as the server side of the stack, and parameter inspectors are not the exception.
Parameter Inspectors are pretty cool because they allow you to "see" (i.e. inspect) arguments and return values from operation calls both at the server and client side, and are implemented through the IParameterInspector interface in the System.ServiceModel.Dispatcher namespace. Here's the definition of IParameterInspector:
public interface IParameterInspector
void AfterCall(string operationName, object outputs, object returnValue, object correlationState);
object BeforeCall(string operationName, object inputs);
On the Server-Side
On the operation dispatcher side of things (the server-side), the BeforeCall() method is called just after the message has been deserialized into the operation (method) arguments but right before calling the actual operation;the inputs array has all arguments that will be passed into the method.
The AfterCall() method is called right after the server method returns but before the return value (and any ref/out arguments) are serialized to the return message.
The easiest way to introduce your parameter inspector into the processing pipeline (and one I rather like from an API point of view) is to create a custom attribute that implements IOperationBehavior and apply it to the operations you are interested in either at the service contract or service implementation level. To do so, you'll want to override ApplyDispatchBehavior() and introduce the inspector like this:
public class InstrumentedOperationAttribute
: Attribute, IOperationBehavior
// other stuff...
public void ApplyDispatchBehavior(
// other stuff...
} // class InstrumentedOperationAttribute
On the Client-Side
You can also introduce a parameter inspector into the client-side WCF proxy side. It works essentially the same as in the server side scenario: The BeforeCall() method will be called just before the arguments are serialized into the request message, and the AfterCall() method is called once the server response message has been deserialized into .NET types.
On the client side, however, you can't introduce (from what I've experienced) a parameter inspector by using an attribute. Instead, you can do it through code by simply configuring it into the OperationDescription object of the proxy instance before the client channel has been opened, like this:
VoucherServiceClient service = new VoucherServiceClient();
// call the server
The Operation Name
You might have noticed by now that the first argument to BeforeCall() and AfterCall() is operationName. This parameter will tell you which operation is being called when the inspector is fired. One thing to keep in mind, though, is that this refers strictly to the operation name in the contract, not to the name of the actual method being invoked. In other words, it does not refer to the name of the method called on the proxy object (client-side) or being called by the dispatcher on the service implementation (server-side).
So, what you will see here is whatever has been specified in the Name property of the [OperationContract] attribute in the service contract interface. If you don't explicitly specify it, then the method name will be used. So don't depend on it being the same as the method name.
One thing that isn't very clear in the documentation is what exactly is the meaning of the return value of the BeforeCall() method and what the correlationState argument to AfterCall() is. All the documentation says about this is that the BeforeCall() return value is "The correlation state that is returned as the correlationState parameter in AfterCall".
Basically, all we know about it is that the correlationState is an opaque value you can pass to the runtime from BeforeCall() with the assurance that you'll get it passed back intact on the corresponding AfterCall() call in the correlationState argument. This is true for both the client and server side and is easily verifiable by a simple test, but can also be seen in the WCF code (using good old reflector) at the points where parameter inspectors are invoked:
- Client-side: On the BeforeRequest() and AfterReply() methods of the (internal) ProxyOperationRuntime class.
- Server-side: On the InspectInputsCore() and InspectOutputsCore() methods of the (internal) DispatchOperationRuntime class.
Both of these classes are in the System.ServiceModel.Dispatcher namespace.
One interesting aspect here is that you can have multiple IParameterInspector implementations associated with a given operation, each one will be called one after the other, and the runtime keeps track of the corresponding correlationState value for each of the registered inspectors.
Though the documentation isn't very explicit about the topic, from what I've been going through the WCF code using reflector my guess is that correlation state is useful because of one reason: It would appear that you cannot depend on member fields/properties of your IParameterInspector to carry state between the BeforeCall() call and the corresponding AfterCall() call. In other words, a single inspector instance might be used through multiple (possibly concurrently) calls. So any state you need to maintain between BeforeCall() and AfterCall() specific to a given operation invocation you'll probably want to externalize into a single reference you can pass around through the correlationState.
For example, in the InstrumentedParameterInspector class I used above as an example, I need the time the BeforeCall() method was invoked in the AfterCall() method in order to calculate the duration of the operation, so I just pass return a DateTime value from BeforeCall() and unbox it in AfterCall().
public object BeforeCall(string operationName, object inputs)
public void AfterCall(
string operationName, object outputs,
object returnValue, object correlationState
DateTime startTime = (DateTime)correlationState;
TimeSpan timeElapsed = DateTime.Now - startTime;
string instanceName = GetInstanceName(operationName);
Keep in mind though that is is just an [educated] guess on my part, and I can't guarantee this is so, but I would sure love someone to confirm/rectify this part.