Showing posts with label Unity Dependency Injection. Show all posts
Showing posts with label Unity Dependency Injection. Show all posts

Friday, December 18, 2009

Constructor Dependency Injection with a WCF Service using Unity

In a previous article I showed how to use dependency injection to specify in a config file which repository should be used by a non WCF service.

This article will show how to do the same but for a WCF service.

The previous article can be found here.

1. WCF uses interfaces so the first thing we need to do is add an interface to our service to WCF enable it.  Since the service is called MileageClaimService I will call the interface IMileageClaimService. Below is the interface definition decorated with WCF ServiceContract and OperationContract attributes.

namespace Service.Finance.Mileage.Interfaces
{
    [ServiceContract(
        Name = "MileageClaimService",
        Namespace = "http://mycompany.com/services/finance/v1.0.0.0")]
    public interface IMileageClaimService
    {
        [OperationContract(Name = "GetAllClaimsForEmployee")]
        List<DTO.Finance.Mileage.MileageClaim> GetAllClaimsForEmployee(string employeePayrollNumber);

        [OperationContract(Name = "GetClaimById")]
        DTO.Finance.Mileage.MileageClaim GetClaimById(int id);

        [OperationContract(Name = "GetClaimByReferenceNumber")]
        DTO.Finance.Mileage.MileageClaim GetClaimByReferenceNumber(string referenceNumber);

        [OperationContract(Name = "Save")]
        [FaultContract(typeof(DBConcurrencyFault))]
        void Save(ref DTO.Finance.Mileage.MileageClaim claim);

        [OperationContract(Name = "Delete")]
        void Delete(DTO.Finance.Mileage.MileageClaim claim);

        [OperationContract(Name = "SubmitClaim")]
        void SubmitClaim(DTO.Finance.Mileage.MileageClaim claim);
    }
}

Below I’ve added the interface and the ServiceBehavior attribute to the service.

namespace Service.Finance.Mileage
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class MileageClaimService : IMileageClaimService
    {
        IMileageClaimRepository _mileageClaimRepository;
       [InjectionConstructor] 
        public MileageClaimService(IMileageClaimRepository  mileageClaimRepository)
        { 
        }

        // Other service methods go here 
    }
}

2.  We need to create the Unity classes for WCF.
I’ve created a project called Common. In here I’m going to place the Unity WCF classes (there are 3 of them) in a directory called Services\Unity.  You can just copy the code to your own project as I did (thanks to dperez for helping me with this: http://blogs.southworks.net/dperez/2008/11/02/unity-friends-the-wcf-service-host-side/).

InjectionBehavior.cs

using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;

// WCF Unity Injection Behavior
namespace Common.Services.Unity
{
    /// <summary>
    /// Creates services instance by using Unity type resolution.
    /// </summary>
    public class InjectionBehavior : IServiceBehavior
    {
        /// <summary>
        /// Injection container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Initializes a new instance of the InjectionBehavior class.
        /// </summary>
        /// <param name="container">Injection container.</param>
        public InjectionBehavior(IUnityContainer container)
        {
            this.container = container;
        }

        /// <summary>
        /// Provides the ability to pass custom data to binding elements to support the contract implementation.
        /// </summary>
        /// <param name="serviceDescription">The service description of the service.</param>
        /// <param name="serviceHostBase">The host of the service.</param>
        /// <param name="endpoints">The service endpoints.</param>
        /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
        public void AddBindingParameters(
            ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase,
            Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }

        /// <summary>
        /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase"> The host that is currently being built.</param>
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                if (channelDispatcher != null)
                {
                    foreach (var endpointDispatcher in channelDispatcher.Endpoints)
                    {
                        endpointDispatcher.DispatchRuntime.InstanceProvider = new InjectionInstanceProvider(this.container, serviceDescription.ServiceType);
                    }
                }
            }
        }

        /// <summary>
        /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
        }
    }
}

InjectionElement.cs
using System;
using System.Configuration;
using System.ServiceModel.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

// WCF Unity Injection Element
namespace Common.Services.Unity
{
    /// <summary>
    /// Injection element class representing a behavior extension configuration tag.
    /// </summary>
    public class InjectionElement : BehaviorExtensionElement
    {
        /// <summary>
        /// Default Unity configuration section path.
        /// </summary>
        private const string DefaultUnityConfigurationSectionPath = "unity";

        /// <summary>
        /// Initializes a new instance of the InjectionElement class.
        /// </summary>
        public InjectionElement()
        {
            this.UnityConfigurationSectionPath = DefaultUnityConfigurationSectionPath;
        }

        /// <summary>
        /// Gets the type of behavior.
        /// </summary>
        /// <value>Behavior type.</value>
        public override Type BehaviorType
        {
            get
            {
                return typeof(InjectionBehavior);
            }
        }

        /// <summary>
        /// Gets or sets the injection container name.
        /// </summary>
        /// <value>Container name.</value>
        [ConfigurationProperty("containerName")]
        public string ContainerName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the Unity configuration section path to read configuration from.
        /// </summary>
        /// <value>Configuration path.</value>
        [ConfigurationProperty("unityConfigurationSectionPath", DefaultValue = DefaultUnityConfigurationSectionPath)]
        public string UnityConfigurationSectionPath
        {
            get;
            set;
        }

        /// <summary>
        /// Creates a behavior extension based on the current configuration settings.
        /// </summary>
        /// <returns>The behavior extension.</returns>
        protected override object CreateBehavior()
        {
            var unitySection = ConfigurationManager.GetSection(this.UnityConfigurationSectionPath) as UnityConfigurationSection;
            IUnityContainer container = new UnityContainer();
            if (this.ContainerName == null)
            {
                unitySection.Containers.Default.Configure(container);
            }
            else
            {
                unitySection.Containers[this.ContainerName].Configure(container);
            }
            return new InjectionBehavior(container);
        }
    }
}

InjectionInstanceProvider.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;

// WCF Unity Injection Provider
namespace Common.Services.Unity
{
    /// <summary>
    /// Injection instance provider for new service instances.
    /// </summary>
    internal sealed class InjectionInstanceProvider : IInstanceProvider
    {
        /// <summary>
        /// Injection container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Type of the service implementation.
        /// </summary>
        private Type instanceType;

        /// <summary>
        /// Initializes a new instance of the InjectionInstanceProvider class.
        /// </summary>
        /// <param name="container">Injection container.</param>
        /// <param name="instanceType">Type of the service implementation.</param>
        public InjectionInstanceProvider(IUnityContainer container, Type instanceType)
        {
            this.container = container;
            this.instanceType = instanceType;
        }

        /// <summary>
        /// Returns a service object given the specified System.ServiceModel.InstanceContext object.
        /// </summary>
        /// <param name="instanceContext">The current System.ServiceModel.InstanceContext object.</param>
        /// <param name="message">The message that triggered the creation of a service object.</param>
        /// <returns>The service object.</returns>
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return this.container.Resolve(this.instanceType);
        }

        /// <summary>
        /// Returns a service object given the specified System.ServiceModel.InstanceContext object.
        /// </summary>
        /// <param name="instanceContext">The current System.ServiceModel.InstanceContext object.</param>
        /// <returns>The service object.</returns>
        public object GetInstance(InstanceContext instanceContext)
        {
            return this.GetInstance(instanceContext, null);
        }

        /// <summary>
        /// Called when an System.ServiceModel.InstanceContext object recycles a service object.
        /// </summary>
        /// <param name="instanceContext">The service's instance context.</param>
        /// <param name="instance">The service object to be recycled.</param>
        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            this.container.Teardown(instance);
        }
    }
}

3. Now we need to update the app.config file for the service project.  Below shows the configuration to enable the service for Unity dependency injection (it’s exactly the same as in the previous post).

<configSections>
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

<unity>
    <containers>
      <container>
        <types>
           <type type="DataAccess.Finance.Mileage.Interfaces.IMileageClaimRepository,
                            DataAccess.Finance.Mileage.Interfaces"
                    mapTo="EF.DataAccess.Finance.Mileage.Repositories.MileageClaimRepository,
                            EF.DataAccess.Finance.Mileage"/>
        </types>
      </container>
    </containers> 
</unity>

And below is the new configuration to enable the service for WCF using the Unity behavior.

<system.serviceModel> 
    <extensions>
      <behaviorExtensions>
        <add name="unity" type="Common.Services.Unity.InjectionElement, Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <netTcpContextBinding>
        <binding name="netTcpContextBindingConfig" transactionFlow="true" />
      </netTcpContextBinding>
    </bindings>

    <services>
      <service behaviorConfiguration="Service.ServiceBehavior" name="Service.Finance.Mileage.MileageClaimService">
        <endpoint address="" binding="netTcpContextBinding" bindingConfiguration="netTcpContextBindingConfig"
          name="TcpBinding_MileageClaimService" contract="Service.Finance.Mileage.Interfaces.IMileageClaimService" />
        <endpoint address="mex" binding="mexTcpBinding" name="MexTcpBinding_MileageClaimService"
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8734/Services.Finance/MileageClaimService/v1.0.0.0" />
          </baseAddresses>
        </host>
      </service>
    </services>

     <behaviors>
      <serviceBehaviors>
        <behavior name="Service.ServiceBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="false" />
          <unity />
        </behavior>
      </serviceBehaviors>
    </behaviors> 
</system.serviceModel>

Note the behaviorExtensions section contains a Unity entry.  This points to the Unity InjectionElement class you added in the last section. 

Also note the <unity /> line in the serviceBehaviors. 

Both of these are critical to enabling the WCF service to use Unity dependency injection.

Thats it, you can use the wcf service as you would do any wcf service by adding a service reference to your client project and calling the methods on it.  The repository will be injected automatically into the constructor on the service because of the behavior extensions defined in the wcf configuration.

Thursday, December 17, 2009

Constructor Dependency Injection using Unity (Enterprise Library)

In the previous article I discussed using the repository design pattern.  I also showed how to use a service layer which can then call the repository data access layer.  This was done by specifying in code which repository to use in the constructor of the service.

This article will show how to use dependency injection to specify in the web.config / app.config which repository should be used by the service.

The previous article can be found here and you should understand this before continuing.

1. You need to download Enterprise Library 4.1 or higher which contains the dependency injection block called ‘Unity’.

2. You need to add references for Microsoft.Practices.Unity.Configuration.dll and Microsoft.Practices.Unity.dll to the service project.

3. The only change you need to make to the service is to specify which constructor is the one that should be enabled for dependency injection.  You do this by using the [InjectionConstructor] attribute.

namespace Service.Finance.Mileage
{
    public class MileageClaimService
    {
        IMileageClaimRepository _mileageClaimRepository;

         [InjectionConstructor] 
        public MileageClaimService(IMileageClaimRepository  mileageClaimRepository)
        { 
        }

        // Other service methods go here 
    }
}

4. For ease of use, its useful to create a helper class to hide the Unity code that is needed to work out the repository to use for the service.  I’ve called the class ‘UnityDependencyInjectionHelper’ and its best to place it in a common project as it will be reused so I have a project called Common.Service.  You can just copy the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;

namespace Service.Common
{
    public static class UnityDependencyInjectionHelper
    {
        private static IUnityContainer container;

        private static void Init()
        {
            container = new UnityContainer();
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers.Default.Configure(container);
        }

        public static T Resolve<T>()
        {
            Init();
            return container.Resolve<T>();
        }

        public static T Resolve<T>(string name)
        {
            Init();
            return container.Resolve<T>(name);
        }
    }
}

5. To call the service from the client you used to do this:

    // Get the mileage claim repository we want to use Entity Framework or ADO.Net.  
    IMileageClaimRepository repository = new EF.DataAccess.Finance.Mileage.Repositories.MileageClaimRepository(); 
   
    // Initialise the service passing in the data access repository it should use 
    MileageClaimService mileageClaimService = new MileageClaimService( repository ); 
   
    // Call a method on the service  
    DTO.Finance.Mileage.MileageClaim mileageClaimService.GetClaimById( 10 );

but now you would do this:

mileageClaimService = Service.Common.UnityDependencyInjectionHelper.Resolve<Service.Finance.Mileage.MileageClaimService>();

// Call a method on the service   
DTO.Finance.Mileage.MileageClaim mileageClaimService.GetClaimById( 10 );

Notice the difference here is we are not specifying the repository to use, it will be automatically injected into the constructor on the service.

6. Lastly you need to add a few lines to the web.config file so the Unity Helper can find the repository.
  6a. First you need to add the Enterprise Library Unity section to the configSections.

<configSections>
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

  6b. Next you need to add the mapping to the repository.  You do this by adding a Unity block with containers as shown below. You can add this straight after the </configSections>

<unity>
    <containers>
      <container>
        <types>
           <type type="DataAccess.Finance.Mileage.Interfaces.IMileageClaimRepository,
                            DataAccess.Finance.Mileage.Interfaces"

                    mapTo="EF.DataAccess.Finance.Mileage.Repositories.MileageClaimRepository,
                            EF.DataAccess.Finance.Mileage"/>
        </types>
      </container>
    </containers>
  </unity>

The ‘type’ effectively says replace any occurrence of the IMileageClaimRepository interface.  And the mapTo effectively says with this MileageClaimRepository.

If you ever needed to change the MileageClaimRepository that is being used all you need to do is change the mapTo line e.g.

To change the above which is using the Entity Framework repository (EF.DataAccess…..) to use ADO.Net repository (ADO.DataAccess…..) you would end up with:

<unity>
    <containers>
      <container>
        <types>
           <type type="DataAccess.Finance.Mileage.Interfaces.IMileageClaimRepository,
                            DataAccess.Finance.Mileage.Interfaces"

                    mapTo="ADO.DataAccess.Finance.Mileage.Repositories.MileageClaimRepository, 
                            ADO.DataAccess.Finance.Mileage"/>
        </types>
      </container>
    </containers>
  </unity>