Sunday, June 8, 2008

Dependency Injection in WCF Services Part 4

In the last post of these series, I showed a first approach on how to use Castle Interceptors along with WCF Castle Facility to apply cross-cutting concerns in the Service Layer. The Log sample interceptor that I used for this last post was hopefully enough to get the flavour, but I think that a more compelling sample is needed to demonstrate what these interceptors are really capable of.
I won't leave the Enterprise Library though, but instead of the Logging Application Block I'll use the Validation Application Block for this demo.

Let's take the sample that I've been using in these series and add a Save function in the Customer Service.


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)
{
return CustomerDataAccess.GetCustomer(id);
}
public Customer SaveCustomer(Customer customer)
{
return customerDataAccess.SaveCustomer(customer);
}
#endregion
}
}

And the unrealistically simple Customer class goes as follows

namespace DISample.Entity
{
[DataContract]
public class Customer
{
[DataMember]
public int CustomerId { get; set; }
[DataMember]
public String Name { get; set; }
}
}

Now we need to meet the requirement that for saving a Customer its Name must be at least 5 characters long and it cannot be longer than 35 characters... The Validation Application Block gets into the game!

The Validation Application Block added value is that it provides a central repository of rules that can be used across the entire application layers so we don't end up coding the same rule in UI, Service, Data Access...

The rules can be defined either in configuration files or through .NET attributes; for this sample I'll use the latter, particularly, the StringLengthValidator through its attribute.

namespace DISample.Entity
{
[DataContract]
public class Customer
{
[DataMember]
public int CustomerId { get; set; }

[StringLengthValidator(5, 35)]
[DataMember]
public String Name { get; set; }
}
}

The validator is already declared, but we still need to kick it off. The Enterprise Library provides some API helper methods to instatiate validators so as to fire them, for now we'll put that code in the Service implementation.


public class CustomerService:ICustomerService
{
....
#region ICustomerService Members
......
public Customer SaveCustomer(Customer customer)
{
ValidationResults validation = Validation.ValidateFromAttributes<Customer>(customer);
if (!validation.IsValid)
throw new InvalidOperationException();

return customerDataAccess.SaveCustomer(customer);
}
#endregion
}

And the validation requirement is already fulfilled with this code. If we follow this approach though, we would need to produce a similar code snippet for every method in the service and for each parameter that a method receives. In other words, this looks like a situation where an interceptor would be more than welcome :)

Let's remove the validation code from the Save method and create the ValidationInterceptor class.

namespace DISample.Interceptor
{
public class ValidationInterceptor:IInterceptor
{
#region IInterceptor Members

public void Intercept(IInvocation invocation)
{
foreach (object argument in invocation.Arguments)
{
if (argument != null)
{
Validator validator = ValidationFactory.CreateValidator(argument.GetType());
ValidationResults result = validator.Validate(argument);
if (!result.IsValid)
throw new InvalidOperationException();
}

}
}

#endregion
}
}

As easy as it looks; input parameters are available through the Arguments property of the IInvocation interface so we just need to loop through them and use Enterprise Library Helper methods to fire the validators.

You may have noticed that I haven't been very informational throwing an InvalidOperationException with no message; however it would be pretty straightforward to create a custom Exception that will carry the information that the Enterprise Library ValidationResult class provides.

We are almost done, the only thing left is to declare the validation interceptor through Castle configuration file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<components>
<component id="CustomerService"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerService, DISample.Service">
<interceptors>
<interceptor>${ValidationInterceptor}</interceptor>
</interceptors>
</component>
<component id="ValidationInterceptor"
type="DISample.Interceptor.ValidationInterceptor, DISample.Interceptor"/>

<component id="CustomerDataAccess"
service="DISample.DataAccess.ICustomerDataAccess, DISample.DataAccess"
type="DISample.DataAccess.CustomerDataAccess, DISample.DataAccess">
</component>
</components>
</configuration>

So before any method of the Customer class gets call, our interceptor will verify the correctness of the input parameters according to the rules defined through the Validation Application Block. No wonder that there is room for many improvements in that code, but the point that I was trying to make is that the task of apply cross cutting concerns gets much simpler using Castle Interceptors and Enterprise Library Application blocks.

kick it on DotNetKicks.com

0 comments:

Post a Comment

  © Blogger template 'Isolation' by Ourblogtemplates.com 2008

Back to TOP