Dependency Injection and WCF Services

So you’ve written your business and data layer classes such that they support dependency injection via constructor parameters. You’ve written a bunch of unit tests around them, achieved 100% code coverage and even used mocks for your dependencies. Awesome, right? Now you want to stand up some WCF services in front of that super unit tested business layer of yours. Your new WCF services are going to have a dependency on those business layer objects, right? How are you going to get around that? Well, you could use a dependency injection container like Unity, NInject, StructureMap, etc. But wait, now your WCF service classes have a dependency on whatever DI container you’re going to use. That means you’re going to have to bootstrap that container in all of your unit tests for your WCF services. What else can a developer do, right? I mean, WCF service implementation classes have to have a default constructor, right? It’s not long you can do constructor injection on a WCF service, right? Wrong! As it turns out, using some of the WCF extensibility points, it’s not too hard at all to achieve constructor injection with WCF.

WCF provides us an interface, IInstanceProvider, which can be used to control the instantiation of service instances. This is a perfect place to put our calls into Unity or some other DI container. An IInstanceProvider that returns service instances from a Unity container looks like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

usingSystem;usingSystem.Configuration;usingSystem.Reflection;usingSystem.ServiceModel;usingSystem.ServiceModel.Channels;usingSystem.ServiceModel.Dispatcher;usingMicrosoft.Practices.EnterpriseLibrary.Common.Configuration;usingMicrosoft.Practices.Unity;usingMicrosoft.Practices.Unity.Configuration;usingMicrosoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity; namespace WcfTechTransfer.Implementation.Hosting{publicclass UnityInstanceProvider : IInstanceProvider {privatereadonly Type serviceType;privatereadonly IUnityContainer container;// TODO: Configure your Unity container public UnityInstanceProvider(Type serviceType){this.serviceType= serviceType;} #region IInstanceProvider Memberspublicobject GetInstance(InstanceContext instanceContext, Message message){return container.Resolve(serviceType);// This is it, the one and only call to Unity in the entire solution!} publicobject GetInstance(InstanceContext instanceContext){return GetInstance(instanceContext, null);} publicvoid ReleaseInstance(InstanceContext instanceContext, object instance){if(instance is IDisposable)((IDisposable)instance).Dispose();}#endregion}}

The IInstanceProvider implementation is pretty straight forward. We just need to provide a constructor with a parameter for the service type. That’s what we pass into Unity to get our service instance back.

Now that you have your IInstanceProvider, you need to wire it up to each endpoint’s dispatch runtime. This can be accomplished through a service behavior like so:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

usingSystem;usingSystem.Collections.ObjectModel;usingSystem.Linq;usingSystem.ServiceModel;usingSystem.ServiceModel.Channels;usingSystem.ServiceModel.Description;usingSystem.ServiceModel.Dispatcher; namespace WcfTechTransfer.Implementation.Hosting{publicclass UnityInstanceProviderServiceBehavior : IServiceBehavior {#region IServiceBehavior Members publicvoid AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters){} publicvoid ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){ serviceHostBase.ChannelDispatchers.ToList().ForEach(channelDispatcher =>{ ChannelDispatcher dispatcher = channelDispatcher as ChannelDispatcher; if(dispatcher !=null){ dispatcher.Endpoints.ToList().ForEach(endpoint =>{ endpoint.DispatchRuntime.InstanceProvider=new UnityInstanceProvider(serviceDescription.ServiceType);});}});} publicvoid Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){} #endregion}}

Next you need to add that new service behavior to your service. This can be done through a custom service host:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

usingSystem;usingSystem.ServiceModel; namespace WcfTechTransfer.Implementation.Hosting{publicclass UnityServiceHost : ServiceHost {public UnityServiceHost():base(){} public UnityServiceHost(Type serviceType, params Uri[] baseAddresses):base(serviceType, baseAddresses){} protectedoverridevoid OnOpening(){this.Description.Behaviors.Add(new UnityInstanceProviderServiceBehavior());base.OnOpening();}}}

Again, pretty straight forward stuff here. We’re just overriding the ServiceHost’s OnOpening() method and adding a new instance of our service behavior to the behaviors collection.

Finally, the last piece to the puzzle, a custom ServiceHostFactory that will return instances of your custom ServiceHost:

1 2 3 4 5 6 7 8 9 10 11 12 13 14

usingSystem;usingSystem.ServiceModel;usingSystem.ServiceModel.Activation; namespace WcfTechTransfer.Implementation.Hosting{publicclass UnityServiceHostFactory : ServiceHostFactory {protectedoverride ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses){returnnew UnityServiceHost(serviceType, baseAddresses);}}}

Since you have a custom ServiceHostFactory, if you’re hosting in IIS, you’ll need to reference this ServiceHostFactory in your .svc file, or, if you’re using .Net 4.0 and taking advantage of the new file-less activation, reference the ServiceHostFactory in your web.config like so:


That’s all there is to it. Now you just need to update your Unity configuration, register your WCF service contract and implementation, along with any dependencies they have. Of course the code here uses Unity as the DI container, but there’s no reason you couldn’t do the same thing with any other container.

comments powered by Disqus