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>

No comments:

Post a Comment