Saturday, 23 February 2008

WCF Services and Dependency Injection with Castle

You might encounter a situation where you are leveraging the dependency injection design pattern in your application. At first this might seem problematic to implement if you are using WCF Services.

For example, you may have the following classes:

public interface ICustomerRepository

{

    // Code

}

 

public class CustomerRepository : ICustomerRepository

{

    // Code

}

 

[ServiceContract]

public interface ICustomerService

{

    [OperationContract]

    void AddCustomer(AddCustomerRequest addCustomerRequest);

}

 

public class CustomerService : ICustomerService

{

    private readonly ICustomerRepository _customerRepository;

 

    public CustomerService(ICustomerRepository customerRepository)

    {

        _customerRepository = customerRepository;

    }

 

    /// <remarks />

    public void AddCustomer(AddCustomerRequest addCustomerRequest)

    {

        // Do stuff

        // _customerRepository.SomeMethod(someParameter);

    }

}

The problem is that as your WCF Service is instantiated by the WCF plumbing, you don't have any control over the creation of this class. How do you "inject the dependency"? How do you supply the "ICustomerRepository" to the "CustomerService"? If you are using the Windsor Container part of the Castle framework (http://www.castleproject.org/) as your Component Container, it has a Facility that enables us to do this.

Using the "CustomerService" example above, you need to do three things in your Web Services project.

1. Modify the .svc file

Create your "CustomerService.svc" file as follows:

<%@ ServiceHost Service="CustomerService" Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory, Castle.Facilities.WcfIntegration" %>

Note the differences between a standard svc file.

2. Modify your Global.asax

Next you need to instantiate a Windsor Container, do this in a Global.asax file as follows (you might have to add one to your project):

public class Global : HttpApplication

{

    private readonly IWindsorContainer _container =

        new WindsorContainer(new XmlInterpreter());

 

    protected void Application_End(object sender, EventArgs e)

    {

        if (_container != null)

            _container.Dispose();

    }

 

    protected void Application_Start(object sender, EventArgs e)

    {

        WindsorServiceHostFactory.RegisterContainer(_container.Kernel);

    }

}

3. Configure Castle

Configure Castle in the usual way, you need to include your "CustomerService" and "CustomerRepository" classes in the configuration. An example is as follows:

<configSections>

  <section

    name="castle"

    type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

</configSections>

 

<castle>

  <components>

    <component

      id="CustomerRepository"

      service="Acme.PetSop.ICustomerRepository, myAssembly"

      type="Acme.PetSop.CustomerRepository, myAssembly" />

    <component

      id="CustomerService"

      service="Acme.PetSop.ICustomerService, myAssembly"

      type="Acme.PetSop.CustomerService, myAssembly" />

  </components>

</castle>

Please note that your "Service" value in the .svc file must match the "id" value in the Castle configuration.

You need to have the "Castle.Facilities.WcfIntegration.dll" assembly referenced by your Web Application project. At the time of writing this assembly is not in the latest Castle release and can only be found in the nightly builds.

The Castle documentation for the WCF Facility can be found here: http://www.castleproject.org/container/facilities/trunk/wcf/index.html


About Me