Tom Hollander's blog

patterns, practices and pontification

Invoking the Policy Injection Application Block at WCF Service Boundaries

Invoking the Policy Injection Application Block at WCF Service Boundaries

  • Comments 9

Back when we originally designed the Policy Injection Application Block for Enterprise Library 3.0, we made a conscious decision not to target the block at WCF service boundaries. This wasn't because we didn't think there was value at injecting cross-cutting concerns at service boundaries - in fact it's one of the most interesting places to do this. However we steered clear of this because WCF already has its own very powerful extensibility model that allows you to accomplish the same thing without using PIAB. In fact we exploited this in Enterprise Library with our Validation and Exception Shielding Behaviors. These behaviours plug in to the WCF pipeline and call the appropriate application blocks as requests come in to the service. We also built PIAB call handlers for these same blocks, but these were strictly for use beneath the service boundary.

However since rejoining the real world I've often wanted to apply PIAB handlers directly at WCF service boundaries. The main reason is that I want the exact same functionality as I'm using elsewhere in the application, and it seems like a waste of time to re-implement the same functionality using WCF Behaviors. Also because the Behavior extensibility model is so, well, extensive, I've found that it isn't as easy to build them as it is to build PIAB handlers.

So why can't you just apply PIAB policies on your service implementation class? Well you can - but remember that for any PIAB policies to take effect, you need to create or wrap your instances using the PolicyInjection.Create and Wrap methods. In most situations this isn't a big deal, but at WCF service boundaries you don't get this opportunity as the instances are created by WCF instead of by you. And so my policies were worthless.

Or so I thought - until I realised that the WCF Behaviors that I was trying to avoid were actually the key to getting what I wanted. It turns out that WCF lets you replace its implementation of IOperationInvoker (which is responsible for calling your service's methods) with your own implementation. In my case I simply build an implementation that wraps the service implementation class with a PIAB proxy before deferring to the original invoker. My implementation looks like this:

    public class PolicyInjectionInvoker : IOperationInvoker
    {
        private readonly IOperationInvoker _innerInvoker;
        private readonly Type _contractType;


        public PolicyInjectionInvoker(IOperationInvoker innerInvoker, Type contractType)
        {
            _innerInvoker = innerInvoker;
            _contractType = contractType;
        }

        #region IOperationInvoker Members

        public object[] AllocateInputs()
        {
            return _innerInvoker.AllocateInputs();
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            PolicyInjector injector = (new PolicyInjectorFactory()).Create();

            object wrappedInstance = injector.Wrap(instance, _contractType);
            return _innerInvoker.Invoke(wrappedInstance, inputs, out outputs);
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            PolicyInjector injector = (new PolicyInjectorFactory()).Create();
            object wrappedInstance = injector.Wrap(instance, _contractType);
            return _innerInvoker.InvokeBegin(wrappedInstance, inputs, callback, state);
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            PolicyInjector injector = (new PolicyInjectorFactory()).Create();
            object wrappedInstance = injector.Wrap(instance, _contractType);
            return _innerInvoker.InvokeEnd(wrappedInstance, out outputs, result);
        }

        public bool IsSynchronous
        {
            get { return _innerInvoker.IsSynchronous;  }
        }

        #endregion
    }

You may have noticed that I'm using the PolicyInjector class rather than the usual PolicyInjection static façade. This was necessary since the PolicyInjection class uses generics, which requires knowing the target type at compile time. In this case I don't know the type, so I need to fall back to the old school non-generic version.

The next step is to tell WCF to plug my PolicyInjectionInvoker into my service's pipeline. This is where Behaviors come in. There are several types of Behaviors, but in this case I need to build an IOperationBehavior since that's the one that lets me replace the Invoker for any given operation. However Operation Behaviors need to be applied specifically to each operation. While this can be useful, in most cases I want to apply the same behaviours to all operations in my contract. To make this simpler I also decided to implement IContractBehavior to automatically apply the operation behaviour to all operations in my contract. My Behavior class looks like this:

    public class WrapForPolicyInjectionBehavior : Attribute, IOperationBehavior, IContractBehavior
    {
        #region IOperationBehavior Members

        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
        {
            
            dispatchOperation.Invoker = new PolicyInjectionInvoker(dispatchOperation.Invoker, operationDescription.DeclaringContract.ContractType);

        }

        public void Validate(OperationDescription operationDescription)
        {
        }

        #endregion

        #region IContractBehavior Members

        public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
        {

            foreach(OperationDescription opDescription in contractDescription.Operations)
            {
                opDescription.Behaviors.Add(this);
            }
        }

        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
        }

        #endregion
    }

As you can see, I only needed to implement the ApplyDispatchBehavior methods in this case, and the code is pretty trivial. All that remains now is to apply the behaviour to my service implementation class, like this:

    [WrapForPolicyInjectionBehavior]
    public class TestService : ITestService
    {
         // I could have put the [WrapForPolicyInjectionBehavior] attribute on specific operations instead

        [ExceptionCallHandler("Service Interface Policy")] // Or, apply policies using config if you prefer
        public bool DoStuff(string action)
        {
            // Do Stuff
        }

    }

That's all there is to it (but yes, I'll post this to EntLibContrib when I get time!). I'm also working on a new, improved exception shielding implementation built on PIAB. But that's a story for another day.

  • Tom,

    IOperationInvoker is very useful.  I found that it allowed me to overcome some limitations in the VAB WCF integration as well.

    http://www.hausertechnologies.com/wordpress/?p=40

  • "at WCF service boundaries you don't get this opportunity as the instances are created by WCF instead of by you. And so my policies were worthless."

    It is actually possible to provide WCF with your own service instances in place of the default ones provided by WCF.  See the following post for details:

    http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html

    One potential benefit of wrapping the service instance as it's being created is now the lifetime of your proxy is the same as the lifetime of your service (per-call, per-session, or singleton).  That probably doesn't matter for policy injection, but using IOperationInvoker means your wrapper is always instantiated per-call, even if the service it wraps is instantiated per-session or as a singleton.

  • Hi,

    I'm not sure about method InvokeBegin and InvokeEnd.

    Why not passing the wrappedInstance to InvokeBegin and InvokeEnd ?

    return _innerInvoker.InvokeBegin(wrappedInstance, inputs, callback, state);

    return _innerInvoker.InvokeEnd(wrappedInstance, out outputs, result);

    Thanks.

  • Philippe - good catch (and you showed me up for not testing the async route :-). I fixed the post according to your suggestion.

  • Yes indeed. WCF is IMHO no more than aspect orientation applied to services through behaviors, predefined transports and so on (this is not meant in a negative way).

    Behaviors and policy injection are thus very similar and reusing generic policies instead of creating new WCF-specific behaviors is probably a very wise thing to do.

    Nice post!

  • 1- WrapForPolicyInjectionBehavior class should have the Attribute suffix.

    2- PolicyInjectionInvoker could create the PolicyInjector only once.

    3- Good job!

  • Hi Tom,

    Excellent post!!!. One thing, as Oran mentioned, WCF allows you to control when a service implementation is created, you only have to develop a custom IInstanceProvider behavior, which seems to be very simple to do.

    Regards,

    Pablo.

  • Last month I posted an entry describing how to invoke the Policy Injection Application Block at WCF service

  • Hi Tom,

    Could you please tell on how would you do this via a behavior extension element. i.e instead of decorating the contract with attribute [WrapForPolicyInjectionBehavior], how can it be specified in the service config file?

    Thanks!!

Page 1 of 1 (9 items)