AOP for the masses

21 views
Skip to first unread message

Michael Hart

unread,
Jun 24, 2008, 10:54:41 PM6/24/08
to nin...@googlegroups.com
I've created a bunch of 3.5 extensions that allow you to intercept
based on lambdas (without attributes). Here's the prettiness:


InterceptBefore<MyObject>(o => o.Load(), i => Debug.WriteLine("About
to call MyObject.Load()"));

InterceptAfter<MyObject>(o => o.Get(), i => Debug.WriteLine("Just got:
" + i.ReturnValue));

InterceptMethod<MyObject>(o => o.Apple(), i =>
Debug.WriteLine("Swallowed Apple!"));

InterceptMethod<MyObject>(o => o.Load(), i => { BeforeLoad();
i.Proceed(); AfterLoad(); });


Firstly, this uses static interceptor registration, so it should be a
lot more performant than existing dynamic methods. Secondly, because
it's lambdas, it has nice typing and refactoring support!

Here are the caveats:

* It's currently using extension methods on IModule, so you'll have to
use "this.InterceptBefore" during IModule.Load(). The alternative
would be to extend the StandardModule and add these methods there, but
that felt a bit too intrusive.

* The arguments of the lambda method don't matter, but the compiler
will require them there if the method needs them, which can look a
little messy. ie, you will have to do something like:

InterceptBefore<MyObject>(o => o.GetChild("", "", 0), i =>
Debug.WriteLine("Getting child"));

Because the extension methods are just looking at the lambda
expression to pull out the MethodInfo, the arguments don't matter and
won't affect the context in which the interception occurs (although
that may be a nice extension) - so you can just use defaults or
whatever.

* I just typed it up then and did a quick basic test, so I have no
idea if it works in more complicated scenarios.


Also, even though the extension methods themselves require 3.5 for the
LINQ expression support, the module and registering of method
interceptors can all be done in 2.0 - you just need to have the
MethodInfo of the method you want to intercept.

All the code should be attached - again in a single file with little
commenting (look at InterceptorExtensions for a starting point)

Enjoy,

Michael

MethodInterceptorModule.cs

Michael Hart

unread,
Jun 26, 2008, 12:28:41 AM6/26/08
to nin...@googlegroups.com
May as well add this too:

InterceptAround<MyService>(s => s.Load(),
i => Debug.WriteLine("Before load"),
i => Debug.WriteLine("After load"));

Here's the code, just chuck it into the InterceptorExtensions class I
posted previously:

public static void InterceptAround<T>(this IModule module,
Expression<Action<T>> methodExpr,
Action<IInvocation> beforeAction, Action<IInvocation> afterAction)
{
var method = GetMethodFromExpression(methodExpr);
var interceptor = new ActionInterceptor(i => { beforeAction(i);
i.Proceed(); afterAction(i); });

module.Kernel.Components.Get<IMethodInterceptorRegistry>().Add(method,
interceptor);
}

Have also been thinking about renaming InterceptMethod to
InterceptReplace, wadya reckon?

Cheers,

Michael

Michael Hart

unread,
Jun 26, 2008, 12:38:22 AM6/26/08
to nin...@googlegroups.com
Actually, let's just clean up the whole InterceptorExtensions class.

Sorry for the noise everyone. Nate - is this the sort of thing you'd
be interested in putting in ninject-contrib?

public static class InterceptorExtensions
{
public static void InterceptReplace<T>(this IModule module,
Expression<Action<T>> methodExpr, Action<IInvocation> action)
{
var method = GetMethodFromExpression(methodExpr);
var interceptor = new ActionInterceptor(action);

module.Kernel.Components.Get<IMethodInterceptorRegistry>().Add(method,
interceptor);
}

public static void InterceptAround<T>(this IModule module,
Expression<Action<T>> methodExpr,
Action<IInvocation> beforeAction, Action<IInvocation>
afterAction)
{

module.InterceptReplace(methodExpr,


i => { beforeAction(i); i.Proceed(); afterAction(i); });
}

public static void InterceptBefore<T>(this IModule module,
Expression<Action<T>> methodExpr, Action<IInvocation> action)
{
module.InterceptAround(methodExpr, action, i => { });
}

public static void InterceptAfter<T>(this IModule module,
Expression<Action<T>> methodExpr, Action<IInvocation> action)
{
module.InterceptAround(methodExpr, i => { }, action);
}

private static MethodInfo
GetMethodFromExpression<T>(Expression<Action<T>> methodExpr)
{
var call = methodExpr.Body as MethodCallExpression;
if (call == null)
{
throw new InvalidOperationException("Expression must be a
method call");
}
if (call.Object != methodExpr.Parameters[0])
{
throw new InvalidOperationException("Method call must
target lambda argument");
}
return call.Method;
}
}

Nate Kohari

unread,
Jun 26, 2008, 7:47:31 AM6/26/08
to nin...@googlegroups.com
Michael:

No problem about the noise in the group; you're working on some cool stuff! If you're interested in creating a project in ninject-contrib, just let me know and I'll get you access to the repository.


Thanks,
Nate
Reply all
Reply to author
Forward
0 new messages