66 msdn magazine Aspect-Oriented Programming
ever, note that RealProxy isn’t a replacement for other AOP tools,
such as PostSharp. PostSharp uses a completely diff erent method.
It will add intermediate language (IL) code in a post-compilation
step and won’t use refl ection, so it should have better performance
than RealProxy. You’ll also have to do more work to implement an
aspect with RealProxy than with PostSharp. With PostSharp, you
need only create the aspect class and add an attribute to the class
(or the method) where you want the aspect added, and that’s all.
On the other hand, with RealProxy, you’ll have full control of
your source code, with no external dependencies, and you can
extend and customize it as much as you want. For example, if
you want to apply an aspect only on methods that have the Log
attribute, you could do something like this:
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
if (!methodInfo.CustomAttributes
.Any(a => a.AttributeType == typeof (LogAttribute)))
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
...
Besides that, the technique used by RealProxy (intercept code
and allow the program to replace it) is powerful. For example, if you
want to create a mocking framework, for creating generic mocks
and stubs for testing, you can use the RealProxy class to intercept
all calls and replace them with your own behavior, but that’s a sub-
ject for another article! Q
BRUNO SONNINO is a Microsoft Most Valuable Professional (MVP) located in Brazil.
He’s a developer, consultant, and author, having written fi ve Delphi books, pub-
lished in Portuguese by Pearson Education Brazil, and many articles for Brazilian
and U.S. magazines and Web sites.
THANKS to the following Microsoft Research technical experts for reviewing
this article: James McCaff rey, Carlos Suarez and Johan Verwey
Figure 17 A Flexible Proxy
class DynamicProxy<T> : RealProxy
{
private readonly T _decorated;
private Predicate<MethodInfo> _filter;
public event EventHandler<IMethodCallMessage> BeforeExecute;
public event EventHandler<IMethodCallMessage> AfterExecute;
public event EventHandler<IMethodCallMessage> ErrorExecuting;
public DynamicProxy(T decorated)
: base(typeof(T))
{
_decorated = decorated;
Filter = m => true;
}
public Predicate<MethodInfo> Filter
{
get { return _filter; }
set
{
if (value == null)
_filter = m => true;
else
_filter = value;
}
}
private void OnBeforeExecute(IMethodCallMessage methodCall)
{
if (BeforeExecute != null)
{
var methodInfo = methodCall.MethodBase as MethodInfo;
if (_filter(methodInfo))
BeforeExecute(this, methodCall);
}
}
private void OnAfterExecute(IMethodCallMessage methodCall)
{
if (AfterExecute != null)
{
var methodInfo = methodCall.MethodBase as MethodInfo;
if (_filter(methodInfo))
AfterExecute(this, methodCall);
}
}
private void OnErrorExecuting(IMethodCallMessage methodCall)
{
if (ErrorExecuting != null)
{
var methodInfo = methodCall.MethodBase as MethodInfo;
if (_filter(methodInfo))
ErrorExecuting(this, methodCall);
}
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
OnBeforeExecute(methodCall);
try
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
OnAfterExecute(methodCall);
return new ReturnMessage(
result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
OnErrorExecuting(methodCall);
return new ReturnMessage(e, methodCall);
}
}
}
public class RepositoryFactory
{
private static void Log(string msg, object arg = null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg, arg);
Console.ResetColor();
}
public static IRepository<T> Create<T>()
{
var repository = new Repository<T>();
var dynamicProxy = new DynamicProxy<IRepository<T>>(repository);
dynamicProxy.BeforeExecute += (s, e) => Log(
"Before executing '{0}'", e.MethodName);
dynamicProxy.AfterExecute += (s, e) => Log(
"After executing '{0}'", e.MethodName);
dynamicProxy.ErrorExecuting += (s, e) => Log(
"Error executing '{0}'", e.MethodName);
dynamicProxy.Filter = m => !m.Name.StartsWith("Get");
return dynamicProxy.GetTransparentProxy() as IRepository<T>;
}
}
Figure 18 A Repository Factory
that Sets the Aspect Events and Filter