Dec 012010
 

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
using System;
using System.Configuration;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity;
 
 namespace WcfTechTransfer.Implementation.Hosting
 {
     public class UnityInstanceProvider : IInstanceProvider
     {
         private readonly Type serviceType;
         private readonly IUnityContainer container;  // TODO:  Configure your Unity container
 
         public UnityInstanceProvider(Type serviceType)
         {
             this.serviceType = serviceType;
         }
 
         #region IInstanceProvider Members
         public object GetInstance(InstanceContext instanceContext, Message message)
         {
             return container.Resolve(serviceType);  // This is it, the one and only call to Unity in the entire solution!
         }
 
         public object GetInstance(InstanceContext instanceContext)
         {
             return GetInstance(instanceContext, null);
         }
 
         public void 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
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
 
namespace WcfTechTransfer.Implementation.Hosting
{
    public class UnityInstanceProviderServiceBehavior : IServiceBehavior
    {
        #region IServiceBehavior Members
 
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters)
        {
        }
 
        public void 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);
                    });
                }
            });
        }
 
        public void 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
using System;
using System.ServiceModel;
 
namespace WcfTechTransfer.Implementation.Hosting
{
    public class UnityServiceHost : ServiceHost
    {
        public UnityServiceHost()
            : base()
        {
        }
 
        public UnityServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }
 
        protected override void 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
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
 
namespace WcfTechTransfer.Implementation.Hosting
{
    public class UnityServiceHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new 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:

1
<!-- Using configuration based activation so we don't need to have .svc files -->

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.

Leave a Reply