WCF Extensibility part 4: Applying formatting to operations

In the previous part we inspected the way you can influence the transition from a service operation to a WCF Message object. This was accomplished with an implementation of the IClientMessageFormatter interface, in our case the SourceRconOperationFormatter class. Now it’s time to look at how to apply this message formatter to the client channel stack.

image

The message formatting happens based on the formatter that is specified as part of the description of an operation. This means that in theory you could change the formatter from operation to operation. In order to get the formatter in place you need to change the Formatter property of the System.ServiceModel.Description.OperationDescription for the operation of your choice. Obviously this has to be done before the first method call is made. The extensibility point that allows you define which formatters are used for client and service operations is an operation behavior. When applied an operation behavior will influence the way an operation behaves when called. Hence the name.

Operation behaviors must implement the System.ServiceModel.Decription.IOperationBehavior interface. First of all look at that interface definition:

public interface IOperationBehavior
{
    void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters);
    void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation);
    void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation);
    void Validate(OperationDescription operationDescription);
}

As you can see it has two methods to apply the specifics of your behavior to an operation description: one for client-side and one for service-side operations. We need to fill in the client-side proxy part of it only. Furthermore, the behavior can validate the operation to which it is applied. More on this in a moment. And finally, should the behavior need to add specific parameters to the entire binding (which can be accessed later on from the binding context) it is free to do so in the AddBindingParameters method.

The SourceRconOperationFormatterBehavior class explicitly implements the IOperationBehavior interface. There is no point to have public method as it is unlikely that the implementation will be called other than via the interface.

public class SourceRconOperationFormatterBehavior: IOperationBehavior
{
    #region IOperationBehavior Members

    void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
        // Nothing for us to do
    }

    void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
        // Apply our own formatter
        clientOperation.Formatter = new SourceRconOperationFormatter(operationDescription);

        // This behavior takes care of both request and replies at client
        clientOperation.SerializeRequest = true;
        clientOperation.DeserializeReply = true;
    }

    void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
    {
        // Client-side only
        throw new NotImplementedException();
    }

    void IOperationBehavior.Validate(OperationDescription operationDescription)
    {
        // Always valid?
        // In our case we cannot have ref or out parameters
        // We could check that parameters are primitive types
    }

    #endregion
}

The ApplyClientBehavior method set the Formatter property of the client operation to which the behavior is applied. It also expresses that the client operation will serialize and deserialize both request and reply messages.

Inside the Validate method we could check whether the operation is compatible with the behavior. For example, we should check that there are no ref or out parameters. We could check that the parameters are either representable as a string, and whether the return value is either of type string (the reply messages always contain string data) or that it is deserializable from a string. I have not been able to get the validate method to be triggered and all implementations of IOperationBehavior inside the framework have an empty method body. With Reflector you can see that the client validations on behaviors should go off when the ChannelFactory is opening. I’m not sure why it does not fire.

You can go the extra mile and have your behavior derive from Attribute:

[AttributeUsage(AttributeTargets.Method)]
public sealed class SourceRconOperationFormatterBehaviorAttribute : Attribute, IOperationBehavior

so it can be used as an attribute above a method:

[ServiceContract]
public interface IRcon
{
    [OperationContract(Action = "status")]
    [SourceRconOperationFormatterBehavior]
    string GetStatus();

You would probably want to change the name of the behavior/attribute class to something like SourceRconOperationAttribute so it looks like [SourceRconOperation]. This makes more sense. In our case the behavior is not really optional as we need to apply the operation formatter for every operation in a service contract to make things work. Having an interface that is only partially usable for remote control seems weird.

Having made the remarks above on how behaviors are non-optional, the next topic will be about a streamlined way to accomplish that all operations in a service contract will have the operation formatter behavior applied.

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s