For a new project at work I thought it best to use dependency injection to deal with some complexities we were facing.  I also wanted to get in and do some mocking in our unit tests to get a good feel for that.  Since we use the Enterprise Library I decided it would be easiest to roll with using Unity.  I have previously blogged about using Unity but this would go even further, because we would be injecting a dependency into the constructor of the service as well as injecting dependencies into the service’s core library.

I did quite a bit of homework and reached the conclusion that in order to do this you would need to hook into WCF’s pipeline and override several key areas during the service host creation and service instance creation.  The best article I found to get me started was this one by Steve Moseley.  Steve broke it down into 6 steps:

  1. Creating a custom instance provider that resolves the service
  2. Creating a custom service behavior to plug in the new instance provider
  3. Creating a custom service host that will add the new behavior
  4. Creating a custom service host factory (which configures your Unity Container)
  5. Changing the service host factory in the .svc file
  6. Inject your objects (through configuration)

His instructions were fantastic for steps 1-5 but step 6 left me to think on my own a bit.  The constructor of the new service we were working on needed an instance of its “core” library which will be the real work horse for the service.  Typically our services only provide a gateway into some other functionality so the service layer handles messaging and translation then delegates the work to a core library which can do whatever it needs to. 

This service would also be a little different than most because we would be relying on both internal and external “suppliers” to provide data to our core library.  These suppliers could be in the form of remote web services, local services, or shared assemblies.  Our core library itself will also need it’s dependencies injected at runtime.

Step 6 involved a bit more detailed study of Unity in which I did the following:

  • Defined typeAliases for each of the dependencies I would be injecting
  • Defined type mappings for the simple dependencies which had only a default constructor
  • Defined typeConfigs for the complex dependencies which required other dependencies passed to their constructor

I chose the constructor injection route because the service should not be created without a core library and the core library should not be created without its supplier dependencies and data access layer.  I believe I could have accomplished something similar had I used default constructors and exposed read/write properties for the dependencies.  This would have removed the constructor requirements but potentially caused issues if someone were using this class without supplying the dependencies to the read/write properties.  So, constructor injection wins for now.

In the end the config looked something like this:

<configSections>
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<unity>
  <typeAliases>
    <!-- Lifetime manager types -->
    <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <typeAlias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager, Microsoft.Practices.Unity, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <!-- User-defined type aliases -->
    <typeAlias alias="ISupplier" type="MyService.Contracts.ISupplier, MyService.Contracts"/>
    <typeAlias alias="MyService" type="MyService.Service.MyService, MyService.Service"/>
    <typeAlias alias="ICore" type="MyService.Contracts.ICore, MyService.Contracts"/>
  </typeAliases>
  <containers>
    <container>
      <types>
        <type type="ISupplier" mapTo="MyService.Suppliers.ExternalSupplier, MyService.Suppliers" name="ExternalSupplier"/>
        <type type="ISupplier" mapTo="MyService.Suppliers.InternalSupplier, MyService.Suppliers" name="InternalSupplier"/>

        <type type="ICore" mapTo="MyService.Core.MyCore, MyService.Core" name="MyCore">
          <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
            <constructor>
              <param name="externalSupplier" parameterType="ISupplier">
                <dependency name="ExternalSupplier"/>
              </param>
              <param name="internalSupplier" parameterType="ISupplier">
                <dependency name="InternalSupplier"/>
              </param>
            </constructor>
          </typeConfig>
        </type>

        <type type="MyService" mapTo="MyService.Service.MyService, MyService.Service">
          <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
            <constructor>
              <param name="myCore" parameterType="ICore">
                <dependency name="MyCore"/>
              </param>
            </constructor>
          </typeConfig>
        </type>
      </types>
    </container>
  </containers>
</unity>  

Once this was working successfully I took a step back to the unit tests project to do some mocking.  I know someone out there is thinking “why would he try that before unit tests?   Couple reasons: 1) This was an entirely new endeavor and its easier for me to get the final concept working 2) The first 5 steps from Steve’s article required some low-level work to hook into WCF.  Rather than unit testing that I decided to flush it out by actually firing up a sample service to work out the kinks.  Next time the unit tests will be done first…