Click here to Skip to main content
Click here to Skip to main content

Dynamic Proxy Class (inject code routines dynamically in methods): Part - I

, 14 Dec 2011
Rate this:
Please Sign up or sign in to vote.
Approaches to implement AOP principles in C#.

Introduction

In my last project I used the Spring.Net framework. Everything in Spring.Net was understandable to me but the AOP thing is what I was most interested in: what they do to call interceptors, and how intercepting classes intercepts my method calls and execute the routine of those interceptors.

Scenario

Suppose I have to call some Before and After routine on every method of a class. Let's see how to implement this using OOP concepts.

public interface IMath
//interface of IMath contains (Basic Add and Subtract Functions)
{
     int Add(int a, int b);
     int Subtract(int a, int b);
}

public class MathClass : IMath
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    public int Subtract(int a, int b) {
        return a - b;
    }
}
//Proxy Class of MathClass
public class MathClassProxy : IMath {

    private MathClass obj = new MathClass();
    public int Add(int a, int b)
    {
        Before(a, b);             //Before function call
        int value = obj.Add(a, b);
        After(value,a, b);       // After function call
        return value;
    }
    public int Subtract(int a, int b) 
    {
        Before(a, b);       //Before function call
        int value = obj.Subtract(a, b);
        After(value,a, b);   //After function call
        return value;
    }
    void Before(int a, int b)
    { 
        // some before routine
    }
    void After(int returnValue,int a, int b)
    { 
        // Some after routine
    }
}

In the above code, we make the interface IMath which has two simple functions: Add and Subtract. Then we make the MathClass that implements IMath. To call the Before and After routine, we make the proxy class, that is MathClassProxy which also implements IMath. In MathClassProxy, we compose the object of MathClass and in the implementation of IMath, we call the Before and After routines.

Problem

Here is the question: if we don’t want to create this proxy class, how can we automatically call the before and after routines on every method? I shuffled OOP concepts, and oops I couldn’t find any.

Solution

OK, let's get this thing straight. This thing will not be implemented via inheritance, overriding, overloading, encapsulation, composition, aggregation, or any thing I know in OOP. How will this thing work then? If somehow I am able to generate the proxy class dynamically then that will work. So I started Googling how to generate a dynamic runtime class.

After Googling for a few hours, I found two solutions to implement this functionality: one via ContextBoundObject, and the second by generating a proxy class dynamically which we will discuss in the next part of this article.

Before implementing the solution, let's create Before and After classes which will contain the logic of what will happen when before and after interceptions occur.

public interface IBefore
{ 
    void Before(MethodBase method, object[] args);
}

public interface IAfter
{
    void After(object returnValue, MethodBase method, object[] args);
}

public class BeforeProcess : IBefore
{
    #region IBefore Members

    public void Before(MethodBase method, object[] args)
    {
        // some code routine
    }
}

public class AfterProcess : IAfter
{
   public void After(object returnValue, MethodBase method, object[] args)
    {
        // some code routine
    }
}

ContextBoundObject

Objects that reside in a context and are bound to the context rules are called context-bound objects. A context is a set of properties or usage rules that define an environment where a collection of objects resides. The rules are enforced when the objects are entering or leaving a context.

Contexts are created during object activation. A new object is placed into an existing context or into a new context created using the attributes included in the metadata of the type. Context-bound classes are marked with a ContextAttribute that provides the usage rules. The context properties that can be added include policies regarding synchronization and transactions.

Let us see how we can address the above problem by using ContextBoundObject. Well, to intercept the methods dynamically, we must inherit the concerned class from ContextBoundObject. In our case, we have MathClass so we need to inherit it from ContextBoundObject.

[Interception]
public class MathClass : ContextBoundObject
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    public int Subtract(int a, int b)
    {
        return a - b;
    }
}

As you can see, there is an attribute named Interception on MathClass, and this Interception attribute is inherited by ContextAttribute, and ContextAttribute is only invoked when its is on a ContextBoundObject. So when this attribute gets invoked, .NET Framework calls GetPropertiesForNewContext to allow you to add IContextProperty objects that you want to use with this Context.

[AttributeUsage(AttributeTargets.Class)]
public class InterceptionAttribute : ContextAttribute
{
    public InterceptionAttribute() : base("Interception") { }
    public override void GetPropertiesForNewContext(IConstructionCallMessage ccm)
    {
        ccm.ContextProperties.Add(new InterceptionProperty());
    }
}

So on GetPropertiesForNewContext, we add the InterceptionProperty class object which is used to add policies regarding synchronization and transaction.

public class InterceptionProperty : IContextProperty, IContributeObjectSink
{
#region IContextProperty Members

    public void Freeze(Context newContext)
    {

    }

    public bool IsNewContextOK(Context newCtx)
    {
        return true;
    }

    public string Name
    {
        get { return "InterceptionProperty"; }
    }

#endregion

#region IContributeObjectSink Members

    public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
    {
        return new InterceptionMainAspect(nextSink);
    }

#endregion
}

The InterceptionProperty class implements two interfaces: IContextProperty and IContributeObjectSink. If you look at IContextProperty members, we have:

  1. Freeze is called when the context itself is frozen.
  2. IsNewContextOK is called after all context attributes have added their context properties to allow your context property to check for dependencies.
  3. Name only returns the ContextPropertyName.

And to create a sink to intercept calls to this object, this class needs to implement IContributeObjectSink which contains the method name GetObjectSink that returns the IMessageSink object.

What is IMessgeSink? When a method call is made on the proxy, the remoting infrastructure provides the necessary support for passing the arguments to the actual object across the remoting boundaries, calling the actual object method with the arguments, and returning the results back to the client of the proxy object.

A remote method call is a message that goes from the client end to the server end and possibly back again. As it crosses remoting boundaries on the way, the remote method call passes through a chain of IMessageSink objects. Each sink in the chain receives the message object, performs a specific operation, and delegates to the next sink in the chain. The proxy object contains a reference to the first IMessageSink it needs to use to start off the chain.

Finally, there is one more class that completes the whole implementation.

public class InterceptionMainAspect : IMessageSink
{
    internal InterceptionMainAspect(IMessageSink next)
    {
        m_next = next;
    }

    #region Private Vars
    private IMessageSink m_next;
    #endregion // Private Vars

    #region IMessageSink implementation
    public IMessageSink NextSink
    {
        get{return m_next;}
    }

    public IMessage SyncProcessMessage(IMessage msg)
    {
        Before((IMethodMessage) msg);
        IMessage returnMethod = m_next.SyncProcessMessage(msg);
        After((IMethodReturnMessage)returnMethod);
        return returnMethod;
    }

    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
    {
        throw new InvalidOperationException();
    }
    #endregion //IMessageSink implementation

    void Before(IMethodMessage msg)
    {
        
        if (msg == null) return;
        
        IBefore beforeObj = (Activator.CreateInstance(typeof(BeforeProcess))) as IBefore;
        beforeObj.Before(msg.MethodBase,msg.Args);

    }
    void After(IMethodReturnMessage returnMsg)
    {
        if (returnMsg == null) return;
        
        IAfter afterObj = (Activator.CreateInstance(typeof(AfterProcess))) as IAfter;
        afterObj.After(returnMsg.ReturnValue,returnMsg.MethodBase,returnMsg.Args);
    }
}

As you can see, on calling the actual method (which is called by m_next.SyncProcessMessage(msg)) we made the Before and After interceptions. And in these intercepting methods, we actually create an instance of the BeforeProcess and AfterProcess classes which we created earlier.

This is how we implement an AOP feature by ContextBoundObject. In the next part, we will discuss how we can implement it by generating a proxy class on runtime.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

M I developer
Technical Lead
Pakistan Pakistan
<Profile>
<Profession>
I am a Software Engineer from Karachi Pakistan, Being a programmer obviously I love to code but I love to code that adds something new in my knowledge else I do copy paste usually Smile | :)
</Profession>
<Education>
Have done Master and Bachelors of Computer Science from Karachi University Pakistan
</Education>
<Interests>
Anything I found interesting technically or non-technically so nothing specific Wink | ;)
</Interests>
</Profile>

Comments and Discussions

 
QuestionAwesome! PinmemberMarkus Ulbricht21-Dec-11 6:31 
AnswerRe: Awesome! PinmemberM I developer21-Dec-11 8:00 
GeneralMy vote of 5 PinmemberAdnan_Khan16-Dec-11 21:15 
GeneralRe: My vote of 5 PinmemberM I developer16-Dec-11 22:00 
GeneralNice read PinmemberCarelAgain15-Dec-11 19:47 
GeneralRe: Nice read PinmemberM I developer15-Dec-11 21:10 
QuestionThat a cool article PinmvpSacha Barber15-Dec-11 0:47 
AnswerRe: That a cool article PinmemberM I developer15-Dec-11 9:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 15 Dec 2011
Article Copyright 2011 by M I developer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid