Interception and Interceptors in C# (Aspect oriented programming) 转载
Interception and Interceptors in C# (Aspect oriented programming)
16. January 2011 10:38In this post, we see how to define specific actions to be executed before or after the execution of every call to a method in our code. This is also known as “intercepting” the execution of a method and is related to AOP (Aspect Oriented Programming). We will explore two different ways of achieving this:
- Using Microsoft’s Unity framework
- Using the PostSharp library
CastleDynamicProxy is another library allowing you to intercept methods and resembles a lot to the Unity framework’s approach so it will not be described here. You can also use .NET framewrok’s Emit namespace or CodeDom to create your intercepting Proxy objects from scratch but this approach is out of the scope of this post.
Aspect Oriented Programming deals with horizontal concerns in the specifications of your programs. Horizontal concerns are requirements that span through every business logic or specific behavior of your system. Take for example the following figure displaying some typical modules of an e-shop application.
The classes of the system that deal with the system’s business logic (charging, product browsing etc), in a good design, do not replicate logic within each other. Those are “Vertical concerns” since they can be considered unrelated. On the contrary, if we want to provide logging for every method in our classes or want to apply security checks we deal with “Horizontal concerns” since they span throughout all the modules of the system.
Now, if you imagine implementing the “typical” way those concerns (eg the logging mechanism), you will end up decorating a lot of your “Vertical concern classes” with code like the following:
Not very pretty especially if you image replicating this in every class of every “Vertical concern”. This is where Aspect Oriented Programming comes in handy by providing a way of turning those horizontal concerns into “Aspects” which practically means that you find a way of intercepting the execution of those methods adding the extra logic in a single place and thus perform your required actions with as few changes as possible to the initial code.
Microsoft’s Unity Framework (link)
Microsoft’s Unity 2.0 comes within Enterprise Library. It is easy to use with the limitation that you need to have all your objects to intercept created through a factory within your code and also the interceptable classes need to implement an Interface or derive from the MarshalByRefObject class.
So lets suppose you have the following class:
public class ClassToIntercept
{
public int ID { get; set; }
public int GetRandomNumber(int min, int max) {
return new Random().Next(min,max);
}
}
In your code all objects of this class are instantiated with “new ClassToIntercept()”. You have no way to intercept this with the Unity Framework without changing anything in the class or the object creation places. On the contrary if you have built factories for your classes (that is you never create objects with the new keyword but rather ask a method to create them for you – not a bad programming practice for many reasons) such as:
public static class ClassToInterceptFactory
{
public static ClassToIntercept GetClassToIntercept()
{
ClassToIntercept ReturnClass = new ClassToIntercept();
return ReturnClass;
}
}
Now whenever you need a ClassToIntercept object in your code you call the GetClassToIntercept method. Unfortunately, you still cannot intercept the class methods. Your class also needs to derive from MarshalByRef object:
public class ClassToIntercept:MarshalByRefObject
{
public int ID { get; set; }
public int GetRandomNumber(int min, int max) {
return new Random().Next(min,max);
}
}
And now you can change your factory as follows:
public static ClassToIntercept GetClassToIntercept()
{
//ClassToIntercept ReturnClass = new ClassToIntercept();
ClassToIntercept ReturnClass = Intercept.ThroughProxy(new ClassToIntercept(),
new TransparentProxyInterceptor(), new[] { new InterceptionClass() });
return ReturnClass;
}
Interception will work and what will happen will be determined from the InterceptionClass that will be explained in a minute.
If you wanted to avoid deriving from MarshalByRef you could have implemented an interface for your initial class such as:
public interface IClassToIntercept
{
int GetRandomNumber(int min, int max);
}
Have your class implement this interface and your factory return the Interface instead of the class. This would mean that you program your whole software to the Interface and not to the actual class (which is not a bad programming practice either although it cannot be applied easily to any scenario). Nevertheless, if this is the case, then your factory would now be like the following:
public static IClassToIntercept GetClassToInterceptInterface()
{
//IClassToIntercept ReturnClass = new ClassToIntercept();
IClassToIntercept ReturnClass = Intercept.ThroughProxy<IClassToIntercept>(new ClassToIntercept(), new InterfaceInterceptor(),
new[] { new InterceptionClass() });
return ReturnClass;
}
For all those to work you need to have the Enterprise Library installed and add as References Microsoft.Practices.Unity and Microsoft.Practices.Unity.Interceptions. The Interception class is as follows (the Invoke method is the one doing the job!):
public class InterceptionClass:IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces() {
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input,GetNextInterceptionBehaviorDelegate getNext){
Console.WriteLine("Before the method");
var methodReturn = getNext().Invoke(input, getNext);
if (methodReturn.Exception == null)
Console.WriteLine("After the method");
else
Console.WriteLine("Ater the method with exception");
return methodReturn;
}
public bool WillExecute {
get { return true; }
}
}
References
Aspect-Oriented Programming, Interception and Unity 2.0 (Dino Esposito)
Interceptors in Unity (Dino Esposito)
Using PostSharp(link)
PostSharp is a great tool and can achieve the most clean interception possible (meaning no changes in your classes and object generation at all even if you do not your factories for object creation and/or interfaces) but it is not a free library. Rather than creating proxies at runtime, it injects code at compile time and therefore changes your initial program in a seamless way to add method interception.
To achieve the same result you include PostsSharp.dll in your project and then create an attribute such as:
[Serializable]
public sealed class TraceAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("Before the method");
}
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine("After the method");
}
}
Now you can add the following attribute making all methods of ClassToIntercept Intereptable (you can also apply the Trace attribute to classes/methods if you can change the original code):
[assembly: Trace(AttributeTargetTypes = "Interceptions.ClassToIntercept")]
The cool thing in this is that you do not change anything else in your code, so your object can be still generated using the new keyword.
In this post, I have demonstrated two very easy approached in providing method interceptors n your programs. The complete demo project can be downloaded here