Dependency Injection in WCF Services Part 3
In the first post of these series, I explained the problems you may run into when applying Dependency Injection in your distributed service layer, and in the second one I put the theory in practice using Castle Windsor WCF Facility.
Now that our DI container instantiates our services, we can take advantage of other features not necessarily related to Dependency Injection available in the container. Particularly in this post I'll drift away a bit from dependency injection and I'll focus on the interception capabilities that Castle Windsor framework offers.
Let's take the sample that I've been using for these series and say for example that we want to use Enterprise Library Application Loggin Block so as to log every method execution in our service object.
A simple implementation would be to include the logging call in the service implementation.
namespace DISample.Service
{
public class CustomerService:ICustomerService
{
#region properties
private ICustomerDataAccess customerDataAccess;
public ICustomerDataAccess CustomerDataAccess
{
get { return customerDataAccess; }
set { customerDataAccess = value; }
}
#endregion
#region ICustomerService Members
public Customer GetCustomer(long id)
{
Logger.Write("GetCustomer Start");
Customer customer = CustomerDataAccess.GetCustomer(id);
Logger.Write("GetCustomer End");
return customer;
}
#endregion
}
}
So we are leaving the logging responsability to the service class itself... That doesn't sound quite right, does it? The service responsability is to interact with the Data Access layer and/or carry out business process but logging is a cross cutting concern that has nothing to do with the actual service implemenation. Besides, for the sake of code cleanignless and reusability, in my Service code I don't want stuff that is not related to the Service main purpose.
Then, how we can possibly achieve logging in our service object leaving the logging calls out of our service implementation? Well, we might use the Decorator pattern for that. This pattern gives the ability to extend the functionality of a class without changing its orginal implementation.
Let's create the CustomerServiceLogginDecorator logging decorator in order to include logging capabilities to our service.
namespace DISample.Service
{
public class CustomerServiceLoggingDecorator:ICustomerService
{
#region properties
private ICustomerService customerService;
public ICustomerService CustomerService
{
get { return customerService; }
set { customerService = value; }
}
#endregion
#region ICustomerService Members
public Customer GetCustomer(long customerId)
{
Logger.Write("GetCustomer Start");
Customer customer = CustomerService.GetCustomer(customerId);
Logger.Write("GetCustomer End");
return customer;
}
#endregion
}
}
Notice that the decorator implements the same interface that our service implementation; that's the whole point of the Decorator pattern. The service consumers use the service through its interface (ICustomerService) and it's through the DI container that we specify a particular service implementation for that interface. Therefore, we can create our decorator as a service frontend and inject the actual service implementation into the decorator.
<configuration>
<components>
<component id="CustomerService"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerServiceLoggingDecorator, DISample.Service">
</component>
<component id="CustomerServiceImpl"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerService, DISample.Service">
</component>
<component id="CustomerDataAccess"
service="DISample.DataAccess.ICustomerDataAccess, DISample.DataAccess"
type="DISample.DataAccess.CustomerDataAccess, DISample.DataAccess">
</component>
</components>
</configuration>
Notice as well that, since the decorator uses ICustomerService interface as dependency, the underlying implementation of the service remains hidden and therefore we can use the DI container to inject as many ICustomerService decorators as we wish upon the real service implementation. Let's imagine that we want to add an exception decorator...
<configuration>
<components>
<component id="CustomerService"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerServiceExceptionDecorator, DISample.Service">
</component>
<component id="CustomerServiceLogging"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerServiceLoggingDecorator, DISample.Service">
</component>
<component id="CustomerServiceImpl"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerService, DISample.Service">
</component>
<component id="CustomerDataAccess"
service="DISample.DataAccess.ICustomerDataAccess, DISample.DataAccess"
type="DISample.DataAccess.CustomerDataAccess, DISample.DataAccess">
</component>
</components>
</configuration>
Ok, so we got it, there are no longer cross cutting concerns in my service implementation but probably you´ll have already realized that this solution is not the best either. If we carried on with this approach we'd need to create a decorator for each cross cutting concern in each service, it means that we could end up with a number of decorator classes up to (number of Services)*(number of cross cutting concerns) = let´s call this idea off now!
Well, it looks like the Decorator Pattern can be the way but we need something to remove from us the burden of creating for a single aspect a bunch of service specific decorators. Fortunately, castle framework comes to our rescue and offers us the Interceptor functionality which allows to create independent and reusable cross cutting concern implementations.
Let´s create our reusable logging interceptor.
using Castle.Core.Interceptor;
using Microsoft.Practices.EnterpriseLibrary.Logging;
namespace DISample.Interceptor
{
public class LogginInterceptor:IInterceptor
{
#region IInterceptor Members
public void Intercept(IInvocation invocation)
{
Logger.Write(invocation.Method.Name + " Start");
invocation.Proceed();
Logger.Write(invocation.Method.Name + " End");
}
#endregion
}
}
And to start using it, we just have to define it in the castle configuration file.
<configuration>
<components>
<component id="CustomerService"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerService, DISample.Service">
<interceptors>
<interceptor>${LoginInterceptor}</interceptor>
</interceptors>
</component>
<component id="LoginInterceptor"
type="DISample.Interceptor.LoginInterceptor, DISample.Interceptor"/>
<component id="CustomerDataAccess"
service="DISample.DataAccess.ICustomerDataAccess, DISample.DataAccess"
type="DISample.DataAccess.CustomerDataAccess, DISample.DataAccess">
</component>
</components>
</configuration>
That's great, isn't it? We define independent interceptors that can be applied upon any class regardless of its type; Castle will take care of the call flow an interception for us.
To that's it, we have used Castle framework to apply cross cutting concerns in our WCF Service... But that's not what WCF Custom Behaviours are for? Yes, Custom Behaviours get injected in the WCF call stack and in there you can implement some aspects not related to your service implementation like logging, catching, transaction handling, etc. Besides the Enterprise Library 3.1 comes along with a set of Custom Behaviors for WCF that are nothing more that thin wrappers upon the Application Blocks like the Validation Block Custom Behavior. Then, why would we want to build Castle Interceptors if we can use the Enterprise Library WCF custom Behaviors?.
Thinking in terms of the overall application and not only about the service layer, you might decide to apply cross cutting concerns to your web layer, your controller layer, your data access layer, etc. and for that WCF Custom Behaviours have no use. The bottom line is that if you want to use a unique homogeneous mechanish to handle cross cutting concers across all your application layers, WCF Custom Behaviours are not the solution!
Having said that, in the next post of these series I'll show how to implement a more sophisticated Interceptor than the Logging one I explained in this post; Particularly I'll show what it takes to implement an equivalent of the Enterprise Library WCF Validation Custom Behaviour in a Castle Interceptor.

0 comments:
Post a Comment