Usng Unity with a WCF Service

Thursday, August 7, 2008

Introduction

In a provious blog post, I talked about using Unity in a multi-layer web application.  You could see in that example, the Application_OnStart event worked out as a very nice place to configure the Unity Container so it could be used throughout the application.  But what do you do if you have a WCF Service project, and you want to configure your service to access the same container?  In this case, you do not have access to any application events, nor is there a web context you pull from.

Oran Dennison has a nice example of accomplishing this task using Spring.Net, and it turns out there is not much to change if you want to use it with Unity also.

Step 1 - Creating an custom InstanceProvider that resolves the service.

The IInstanceProvider controls the creating and recycling of the service when it is requested.  We can impliment this interface and pass in a UnityContainer and resolve the service that is being requested.

 

    1 using System;

    2 

    3 using System.ServiceModel;

    4 using System.ServiceModel.Channels;

    5 using System.ServiceModel.Dispatcher;

    6 

    7 using Microsoft.Practices.Unity;

    8 

    9 namespace UnitySample

   10 {

   11     public class UnityInstanceProvider : IInstanceProvider

   12     {

   13         public UnityContainer Container { set; get; }

   14         public Type ServiceType { set; get; }

   15 

   16         public UnityInstanceProvider()

   17             : this(null)

   18         {

   19         }

   20 

   21         public UnityInstanceProvider(Type type)

   22         {

   23             ServiceType = type;

   24             Container = new UnityContainer();

   25         }

   26 

   27         #region IInstanceProvider Members

   28 

   29         public object GetInstance(InstanceContext instanceContext, Message message)

   30         {

   31             return Container.Resolve(ServiceType);

   32         }

   33 

   34         public object GetInstance(InstanceContext instanceContext)

   35         {

   36             return GetInstance(instanceContext, null);

   37         }

   38         public void ReleaseInstance(InstanceContext instanceContext, object instance)

   39         {

   40         }

   41 

   42         #endregion

 You can see above when the GetInstance is evoked by a service, the service's type is passed into the contructor and resolved by the UnityContainer.

 Step 2 - Create a custom ServiceBehavior object to plug in our InstanceProvider

Now that we have a cutom instance provider we need to way to insert this new provider at run time, and it is IServiceProvider that is going to provide this for us.  When the ApplyDispatchBehaviour is evoked, the code will loop through the collection of EndPoints in our project and pass the corresponding service type to the instance provider.

    1 using System.ServiceModel;

    2 using System.ServiceModel.Channels;

    3 using System.ServiceModel.Description;

    4 using System.ServiceModel.Dispatcher;

    5 

    6 using Microsoft.Practices.Unity;

    7 using System.Collections.ObjectModel;

    8 

    9 namespace UnitySample

   10 {

   11     public class UnityServiceBehavior : IServiceBehavior

   12     {

   13         public UnityInstanceProvider InstanceProvider

   14         { get; set; }

   15 

   16         private ServiceHost serviceHost = null;

   17 

   18         public UnityServiceBehavior()

   19         {

   20             InstanceProvider = new UnityInstanceProvider();

   21         }

   22         public UnityServiceBehavior(UnityContainer unity)

   23         {

   24             InstanceProvider = new UnityInstanceProvider();

   25             InstanceProvider.Container = unity;

   26         }

   27         public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

   28         {

   29             foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)

   30             {

   31                 ChannelDispatcher cd = cdb as ChannelDispatcher;

   32                 if (cd != null)

   33                 {

   34                     foreach (EndpointDispatcher ed in cd.Endpoints)

   35                     {

   36                         InstanceProvider.ServiceType = serviceDescription.ServiceType;

   37                         ed.DispatchRuntime.InstanceProvider = InstanceProvider;

   38 

   39                     }

   40                 }

   41             }

   42         }

   43 

   44 

   45         public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }

   46 

   47 

   48         public void AddBindingParameters(

   49             ServiceDescription serviceDescription,

   50             ServiceHostBase serviceHostBase,

   51             Collection<ServiceEndpoint> endpoints,

   52             BindingParameterCollection bindingParameters)

   53         {

   54         }

   55 

   56     }

   57 }

 Step 3 - Create a Custom Service Host that will add the new behavior

The ServiceHost object basically provides the mechamism to "load a service, configure endpoints, apply security settings, and start listeners to handle incoming requests."  We can derive a custom ServiceHost class that in our case that will also add the functionality of adding our custom Service Behavior to it.

    1 using System;

    2 using System.ServiceModel;

    3 

    4 using Microsoft.Practices.Unity;

    5 

    6 namespace UnitySample

    7 {

    8     public class UnityServiceHost : ServiceHost

    9     {

   10         public UnityContainer Container { set; get; }

   11 

   12         public UnityServiceHost()

   13             : base()

   14         {

   15             Container = new UnityContainer();

   16         }

   17 

   18         public UnityServiceHost(Type serviceType, params Uri[] baseAddresses)

   19             : base(serviceType, baseAddresses)

   20         {

   21             Container = new UnityContainer();

   22         }

   23 

   24         protected override void OnOpening()

   25         {

   26             if (this.Description.Behaviors.Find<UnitySample.UnityServiceBehavior>() == null)

   27                 this.Description.Behaviors.Add(new UnityServiceBehavior(Container));

   28 

   29             base.OnOpening();

   30         }

   31 

   32 

   33 

   34     }

   35 }

 When the service host starts it will check to see if the custom ServiceBehaviour has already been added and if it has not, then it will be added to Behavior collection.

Step 4 - Creating the Custom ServiceHostFactory

In IIS our Host will be created dynamically, so we need to use the ServiceHostFactory which uses the Factory Pattern behind the scenes to return the custom Service Host we need.

    1 using System;

    2 using System.ServiceModel;

    3 using System.ServiceModel.Activation;

    4 using Microsoft.Practices.Unity;

    5 using Microsoft.Practices.Unity.Configuration;

    6 using System.Configuration;

    7 

    8 namespace UnitySample

    9 {

   10     public class UnityServiceHostFactory : ServiceHostFactory

   11     {

   12         protected override ServiceHost CreateServiceHost(

   13                                           Type serviceType, Uri[] baseAddresses)

   14         {

   15             UnityServiceHost serviceHost = new UnityServiceHost(serviceType, baseAddresses);

   16             UnityContainer container = new UnityContainer();

   17             serviceHost.Container = container;

   18 

   19             //configure container

   20             UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

   21             section.Containers.Default.Configure(serviceHost.Container);

   22 

   23             return serviceHost;

   24         }

   25     }

   26 }

Note that I am using the same Unity Configuration from my previous post.

Step 5 - Add a Reference to ServiceHostFactory in the *.svc file

    1 <%@ ServiceHost Language="C#" Debug="true" Service="UnitySampleService" CodeBehind="~/App_Code/UnitySampleService.cs"

    2     Factory="UnitySample.UnityServiceHostFactory" %>

Step 6 - Inject Your Objects on the Service

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Linq;

    4 using System.Runtime.Serialization;

    5 using System.ServiceModel;

    6 using System.Text;

    7 using UnitySamples.Core.Controllers;

    8 using UnitySamples.Core.Dto;

    9 

   10 // NOTE: If you change the class name "UnitySampleService" here, you must also update the reference to "UnitySampleService" in Web.config.

   11 public class UnitySampleService : IUnitySampleService

   12 {

   13     private IProcessUserController controller;

   14 

   15     public UnitySampleService(IProcessUserController controller)

   16     {

   17         this.controller = controller;

   18     }

   19 

   20     public void DoWork(User user)

   21     {

   22         controller.RegisterUser(user);

   23     }

   24 

   25 }

 

 

 

comments powered by Disqus