Thursday, May 1, 2008

Dependency Injection in WCF Services

To use Dependency Injection in a distributed service layer is not a trivial task. Either if you use Web Services or WCF, your services are created by the .NET engine, so somehow you need to find a way to inject your Dependency Injection Container in there.

You might as well let the engine create your service and then use the Service Locator pattern to hook up the underlying layer.
In the following code snippet we use Castle container as a Service Locator to retrieve the Data Access Layer instance that is used from our WCF service.
    public class CustomerService:ICustomerService
{
#region ICustomerService Members
public Customer GetCustomer(long id)
{
ICustomerDataAccess da;
da = Global.Container.Resolve<ICustomerDataAccess>();
return da.GetCustomer(id);
}

#endregion
}
In the above example, you'll notice that apart from using a slightly different pattern for creating the Data Access object, the service instantiation is not handled by our DI container and this has some bad implications. Some DI containers, such as Castle or Spring.NET, offer AOP features but those features can only be applied upon objects instantiated by the container. So the bottom line is that in this scenario we can't use those AOP frameworks upon our service objects.

So for some scenarios Service Locator pattern could be just alright, but what if we still want to go for a pure Dependency Injection pattern in our service layer? How can we get around the service creation problem?

If you are using Web Services I'm afraid you don't have many options. Comparing to WCF, Web services is not a very configurable technology and the only way to intercept requests is to replace the ASP.NET default HttpHandler with a custom one, which handles the requests/responses and the service object creation. Good news are that if you are using Spring.NET, you don't need to create the handler yourself; there is a custom HttpHandler implementation in the framework that you can use for that purpose.

If you are using WCF in your service layer, congratulations :) WCF is a highly configurable technology and in fact it provides AOP features through the injection of custom behaviors. Using this extensibility Oran gives us the solution to our problem here. As you can read in that post, for creating new service instances WCF uses a factory, which implements IInstanceProvider ... Great news are that we can create a custom behavior that replaces the default IInstanceProvider factory implementation with our own implementation, and in there we can hook up our DI Container.
To cut a long story short, Oran creates an IInstanceProvider implementation that uses Spring.NET container for service objects creation.

If you are not using Spring.NET, don't panic :) there are also implementations available for Castle (created by Ayende) and for ObjectBuilder (created by Pablo M. Cibraro) that follow similar approaches than Oran's. Even if you are using Spring.NET, you might as well use Stan's ammendments to Oran's solution.

To sum up, in this post we have explored some different options to implement Dependency Injection in our Service Layer depending on the distributed technology (WebServices and WCF) and on the DI container (Spring.NET, Castle..) used.

In the next post of these series, I'll put these concepts in practice using the WCF Dependency Injection Castle facility.

kick it on DotNetKicks.com

2 comments:

  1. This is great man, thanks a lot. One of the most useful areas that Dependency Injcection makes sense becomes very complicated, but you explained it quite well.

    Why not write an HTTP Handler on this purpose for unity :)

    ReplyDelete
  2. The Service Locator Pattern is NOT a pattern that is used for Dependency Injection. This is a misconception. The Service Locator Pattern is a "consumption" pattern, not injection. The components that need depedencies have to CALL OUT to get their dependencies (it doesn't matter if the CALL OUT is to a DI container, the point here is the component with the dependency is being forced to make the call. this is wrong in the DI world)

    ReplyDelete