AOP with Autofac and DynamicProxy2
Update: with Autofac2, the DynamicProxy integration has changed. See the wiki for current documentation.
Many developers will be familiar with the use of DynamicProxy from the Castle Project for method interception.
The latest AutofacContrib binary release includes a small library to make using DynamicProxy with Autofac easier.
AutofacContrib.DynamicProxy2 provides functionality for attaching DynamicProxy interceptors to Autofac components.
Setting up the integration requires that the interception module is registered:
builder.RegisterModule(new StandardInterceptionModule());
An example component exposing a single service:
public interface ICalculator
{
int Add(int lhs, int rhs);
int Multiply(int lhs, int rhs);
}
[Intercept("log-calls")]
public class Calculator : ICalculator
{
public virtual int Add(int lhs, int rhs)
{
return lhs + rhs;
}
public virtual int Multiply(int lhs, int rhs)
{
return lhs * rhs;
}
}
Note the Intercept
attribute on the component implementation. The string "log-calls"
is the service providingIInterceptor
that will be attached to the component instances.
In this example, the CallLogger
component will be used to provide the log-calls service:
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.Write("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
The interceptor is just a component like any other. A complete application shows the configuration of the interceptor:
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.Register<Calculator>()
.As<ICalculator>();
builder.Register(c => new CallLogger(Console.Out))
.Named("log-calls");
builder.RegisterModule(new StandardInterceptionModule());
using (var container = builder.Build())
{
var calculator = container.Resolve<ICalculator>();
var result = calculator.Add(1, calculator.Multiply(2, 3));
Console.WriteLine("Complete, program result is {0}.", result);
Console.ReadKey();
}
}
}
The output of the application is:
Calling method Multiply with parameters 2, 3... Done: result was 6.
Calling method Add with parameters 1, 6... Done: result was 7.
Complete, program result is 7.
There are a few things worth noting:
-
CallLogger
has a dependency on theTextWriter
to which the log will be written, and this can be injected as usual -
Interceptors can have factory, singleton or container scope like other components
-
This example could use
Intercept(typeof(CallLogger))]
for exactly the same result, however, using service names keeps implementation type coupling to a minimum -
If you’re lazy and don’t want to register all of your interceptors, you can use the
Intercept(Type)
attribute constructor and set up your container withbuilder.RegisterTypesAssignableTo<IInterceptor>()
-
To use interceptors with expression- or instance-registered components, use the
FlexibleInterceptionModule
instead of the standard one - this will enable proxying through interfaces in these scenarios
If you want to run this yourself, the complete solution is attached to this article.
Kudos to the Castle team for making DynamicProxy available - fantastic work, guys!
A quick aside: if you’re not already subscribed, take a look at Fredrik Kalseth’s excellent blog. His recent series of well-written articles cover application design and unit testing with ASP.NET MVC, Linq to SQL, and Autofac. Anyone looking for some pointers in the right direction with these technologies will need to look no further.