Injecting into a Wcf Service

Update 7/12/2011

This page gets hit a lot, but it’s not very concise. It’s me working through the problem and concluding with “I should’ve done it another way”.

This new post is much better: https://hamletcode.wordpress.com/2011/07/12/wcf-and-unity-revisited/. I rambled a bit… that’s my thing, but at least the code is neater.

Update 9/2/2009: I forgot to include the service contract. Added it.

Overview

When you setup a Service Host, WCF manages the creation of your services. It requires there to be a default constructor so that it can just instantiate the thing and be done with it.

I often find myself wishing to be able to pass something into the object when its created. For example: a unity container. I’d like to inject the unity container.

Lacking that ability, we end up with a static container somewhere that the service calls.

public class TestService : ITestService
{
    public void DoSomething()
    {
        IWhatever whatever = StaticHelper.Container.Resolve<IWhatever>();
    }
}

What’s wrong with that? Statics are evil. Whenever you use a static, there should be a big red flag. Sure, its convenient, but its not very testable or flexible.

Sometime we can somewhat counter the testability limitation as follows

public class TestService : ITestService
{
    private readonly IUnityContainer _unityContainer;

    public TestService()
    {
        // wcf will use this constructor
        _unityContainer = StaticHelper.Container;
    }

    // tests use this constructor
    public TestService(IUnityContainer container)
    {
        _unityContainer = container;
    }

    public void DoSomething()
    {
        IWhatever whatever = _unityContainer.Resolve<IWhatever>();
    }
}

That works, but still smells kind of funny.

Tonight’s adventure was to figure out how to pass a unity container to the test service when instantiated. I didn’t really know where to start on this, so started at the ServiceHost, and googled the night away.

There are a bunch of examples out there to use unity to determine the implementation type of the service. Two examples that I looked at rely on a static IOC container in order to determine the implementation type. In those cases, the statics can be easily eliminated just by passing the container through the layers as I do here. (Their goal’s were different. My goal is to get rid of the statics.)

UPDATE: Now that I’ve gotten it all to work, and I’ve spent some time thinking about the conclusion, the fore mentioned approach is the best way. Rather than pulling objects from unity and assign it to properties, just define the service itself in the container. Then unity can handle all of the injection. I had to write this blog to come to that conclusion, so I’m not going to throw it all away.

Basic Steps

    1. Create a service host 
    2. Create a behavior 
    3. Create an instance provider – this is what we’re really interested in. This creates the service object. We need to get the IUnityContainer to this guy. 
    4. Connect the host to the behavior 
    5. Connect the behavior to the instance provider

 

I created a subclass of ServiceHost and added an IUnityContainer parameter to both of the constructors. The parameter value is saved to a local variable.

The service behavior has no knowledge of the IUnityContainer. It just gives us the bridge between the instance provider.

The instance provider has access to the service host. As you may recall, the service host has the IUnityContainer property. If the service object (implementation) has a IUnityContainer property, then we assign it.

The Code

DISCLAIMER: This code is just enough to get it to work. Its not tied up nice and neat.

An Interface for Services that require an IUnityContainer

The instance provider, way down at the bottom, will see if the service implements this interface. If so, it assigns the property.

namespace AllardWorks.AwBus.Contracts
{
    public interface IUnityWcfService
    {
        IUnityContainer UnityContainer { get; set; }
    }
}

The Service Host

    public class AwBusServiceHost : ServiceHost
    {
        public AwBusServiceHost(IUnityContainer container)
            : base()
        {
            _container = container;
        }

        public AwBusServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
            _container = container;
        }

        protected override void OnOpening()
        {
            Description.Behaviors.Add(new AwBusServiceBehavior());
            base.OnOpening();
        }

        public readonly IUnityContainer _container;
        public IUnityContainer UnityContainer
        {
            get { return _container; }
        }
    }

The Behavior

We’re only interested in the ApplyDispatchBehavior method.

This loops through all of the endpoint dispatchers and sets the instance provider to one of our own design. We pass it the type that is to be instantiated.

    public class AwBusServiceBehavior : IServiceBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher cd = cdb as ChannelDispatcher;
                if (cd == null)
                {
                    continue;
                }

                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = new AwBusInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }

        #region Not Used
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }
        #endregion
    }

The Instance Provider

At last, we get to create an object. Yay.

The interesting part here is the IUnityWcfService. If the new object implements that interface, then we set the UnityContainer property that the interface provides.

    public class AwBusInstanceProvider : IInstanceProvider
    {
        private readonly Type _type;

        public AwBusInstanceProvider(Type type)
        {
            _type = type;
        }

        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }

        public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
        {
            object service = Activator.CreateInstance(_type);
            IUnityWcfService unityService = service as IUnityWcfService;
            if (unityService != null)
            {
                unityService.UnityContainer = ((AwBusServiceHost)instanceContext.Host).UnityContainer;
            }
            return service;
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
        }
    }

Take it for a Test Drive

A Test Service. (Hand coded on the spot. Should be mostly correct though)

[ServiceContract]

public interface IDoSomething

{

[OperationContract]

bool HasContainer();

}

 

public class DoSomething : IDoSomething, IUnityWcfService{

public IUnityContainer UnityContainer { get; set; }

public bool HasContainer()

{

return UnityContainer != null;

}

}

The Server

        private IUnityContainer _unity;
        private ServiceHost _host;
        private void StartServer()
        {
            _unity = new UnityContainer();
            _host = new AwBusServiceHost(_unity, typeof(TestService), new Uri[] { });

            #region Mex
            BindingElement mexBindingElement = new TcpTransportBindingElement();
            CustomBinding mexBinding = new CustomBinding(mexBindingElement);
            ServiceMetadataBehavior metadataBehavior = _host.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (metadataBehavior == null)
            {
                metadataBehavior = new ServiceMetadataBehavior();
                _host.Description.Behaviors.Add(metadataBehavior);
            }
            _host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, ServiceUri + "/MEX");
            #endregion

            #region NetTcp Endpoint
            c.Binding binding = new NetTcpBinding(SecurityMode.None);
            _host.AddServiceEndpoint(typeof(ITestService), binding, new Uri(ServiceUri));
            #endregion

            _host.Open();
        }

The Client

                c.Binding binding = new NetTcpBinding(SecurityMode.None);
                EndpointAddress address = new EndpointAddress(ServiceUri);
                using (ChannelFactory<ITestService> client = new ChannelFactory<ITestService>(binding, address))
                {
                    ITestService proxy = client.CreateChannel();
                    MessageBox.Show(proxy.HasContainer().ToString());
                }

Conclusion

This is pretty neat. This solves an inconvenience we’ve had at work, though at work we need a different object. We have this thing we call “the factory”, which is our own IOC container. We wrote it before unity was released and before we realized that it already a name (IOC).

Once you understand that you can pass anything you want to the service host, and then eventually pass it down to the service object, there are a lot of other injection possibilities. Maybe rather than pass the entire container to the service, you just pull out the objects the service needs an inject them. There are a few ways to do that, but after a few seconds of thought, I think that using unity to construct the service object itself is the best idea. (I found a few examples of this, so its an established approach.)

Advertisements

One Response to Injecting into a Wcf Service

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s

%d bloggers like this: