Update: Fixed some code issue and added a bit more discussion
With our
DC ALT.NET group, we've discussed
IoC quite often as part of the discussion. Some are pretty new to the concepts of using Dependency Injection with these IoC containers. Instead, you'd find people with overloaded constructors all over the place and mocking out the dependencies in the unit tests and so on.
Anyhow, I have two favorite IoC containers, being
Jeremy Miller's StructureMap and
Castle Windsor. If you unfamiliar how to use Castle Windsor, there is a nice set of tutorials
here. I'd have to say StructureMap is the lightest weight of all of them and I like the
fluent interfaces for configuration, but when I need other things as well, like
AOP, I switch to Castle Windsor. Which brings us to today's post.
Why AOP?
With object oriented programming, we try to break down things into small, distinct parts with as little overlap as possible through Separation of Concerns (SoC). However, some of these concerns defy any sort of encapsulation by having cross-cutting concerns. Some of these include logging, transactions, security among other things.
So, what options do we have for AOP in the .NET world? Well, we have three that are mainstream and supported at this point:
- Castle Windsor
- Spring.NET
- PostSharp
Today I'm going to briefly talk about
Windsor Interceptors. It's a powerful concept for being able to intercept calls while using Castle Windsor and quite easy to use. Let's go through a quick sample.
Castle Windsor has an interface that you must implement in order to write an interceptor called IInterceptor that looks like this:
public interface IInterceptor
{
void Intercept(
IInvocation invocation);
}
So, let's create one for a simple logging application:
public class MeddlingInterceptor :
IInterceptor
{
public void Intercept(
IInvocation invocation)
{
Logger.Write(
"Entering method " + invocation.Method.Name +
" with user " +
Thread.CurrentPrincipal.Identity.Name);
// Maybe do some user validation
invocation.Proceed();
// Do some stuff afterwards if need be as well
}
}
And now let's configure it to use with our class we have already defined called EmployeeRepository.
public class EmployeeRepository :
IEmployeeRepository
{
public void Save(Employee employee)
{
// Calls database to save employee
}
}
And inside our config file is this lovely mess:
<component id=”meddlinginterceptor”
service=”Podwysocki.Interceptors.MeddlingInterceptor,
Podwysocki.Interceptors”
type=”Podwysocki.Interceptors.MeddlingInterceptor,
Podwysocki.Interceptors” />
<component id=”employeerepository”
service=”Podwysocki.Interceptors.IEmployeeRepository,
Podwysocki.Interceptorsr”
type=”Podwysocki.Interceptors.IEmployeeRepository,
Podwysocki.Interceptors”>
<interceptors>
<interceptor>${meddlinginterceptor}</interceptor>
</interceptors>
</component>
As you can see, I didn't have to change anything in my EmployeeRepository class at all in order for this to work. It works rather seemlessly. With the IInvocation interface, I can get the method name, the target, arguments and so on. But, I also have the ability to reset any of those arguments as well.
Imagine I want to wrap transactions so that it doesn't appear in my code and just happens behind the scenes. Well, I can do that too through a given interceptor as well.
public class TransactionInterceptor :
IInterceptor
{
public void Intercept(
IInvocation invocation)
{
using(
TransactionScope scope =
new TransactionScope())
{
// Maybe do some user validation
invocation.Proceed();
}
}
}
Now, do I recommend that? Well, that's another story because O/RMs are probably a better way to go with handling that, but still this can be done.
AOP in Domain Driven Design???
In a
previous post, I covered the assertion that proper Domain Driven Design cannot be accomplished without AOP. The assertion is that the domain model needs to stay as clean as possible, so that all other cross cutting concerns such as logging, transactions, security and whatnot do not belong in your model. Instead, AOP needs to be implemented. This of course has been hot topic on the
Domain Driven Design mailing list.
Randy Stafford had a great response on this thread and I thank him for it. He seems to indicate that it's not really necessary for most things and I'd have to agree. To read his responses, they are
here and
here.
Conclusion
A lot of the Domain Driven Design stuff has me rereading Eric Evans book again looking for nuggets I missed the first time around, so it's definitely time to get back reading. I've got way too many books on my pile!