Click here to Skip to main content
15,867,594 members
Articles / Programming Languages / C#

AOP – Aspect Orient Programming in .NET

Rate me:
Please Sign up or sign in to vote.
4.33/5 (8 votes)
20 Apr 2009CPOL2 min read 39.6K   22   12
Use AOP principals to centralize application policy

What is AOP?

All methodologies have always searched for a way to make code more readable & maintainable.
Procedural programming split our code to procedures and OOP divided our world to entities with a "responsibility" to their internal data and operations.

AOP is not a replacement... it tries to give an elegant add-on to those infrastructure parts that are cross modules and/or cross layers of any enterprise application.

AOP in .NET?

In .NET Framework, the key to AOP implementation is - interception.
This concept is mostly known to programmers who wrote applications using multi-threading, remoting, enterprise services (COM+) and of course - WCF.
Interception wraps and separates our objects within a separate context.
This same method allows programmers to define with a simple attribute - transactional behaviour ([Transaction=Required]), thread synchronization ([Synchronization]), check authorization against an Active-Directory ([PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]) etc.

What Is It Good For?

In a recent project I worked on, we were requested to address a very strict regulation with a focus on auditing, authorization and exception management.
Using an infrastructure built using AOP principals, we managed to handle these three and some more in a single layer - forcing a policy across the entire application with no dependency on the 'user' (the programmer in this case) and fully separated from the logical code of the application.
Other uses for this could be performance profiling, session management, in fact - any cross-application policy you wish to implement.
When needed, you can always be more flexible and expose some attributes to allow the 'user' to configure the behaviour of this policy - but - the code remains separated and elegantly wrapped.

How To Implement?

To implement this, you need to inherit ContextBoundObject - this will 'make' our object to be separated in its own private context.

C#
[Intercept]
public class ServiceInterfaceBase : ContextBoundObject

This will cause all calls to our object to become messages (IMessage), this way - when implementing an IMessageSink we could intervene and 'canalize' all the calls to this object (and its ancestors) to pass through our interception layer - there we can check authorization, write to a log, handle exceptions, etc.

Our own "Interception" attribute helps us mark our base class and attach our property to the new context:

C#
[AttributeUsage(AttributeTargets.Class)]
public class InterceptAttribute:ContextAttribute
{
    public InterceptAttribute() : base("InterceptAttribute") { }

    public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
    {
        //add our intercept property to context!!
        IContextProperty interceptProperty = new InterceptProperty();
        ctorMsg.ContextProperties.Add(interceptProperty);
    }

    //Called by the runtime in the creating client's context
    public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
    {
        if (ctx.GetProperty("Interception") == null)
        {
            return false;
        }
          
        return true;
    }
}

Here you can see the context property, here we'll make sure the "message" is sent to our custom "sink":

C#
public class InterceptProperty : IContextProperty, IContributeServerContextSink
{
    #region IContextProperty Members

    public void Freeze(Context newContext)
    {
    }

    public bool IsNewContextOK(Context newCtx)
    {
        InterceptProperty newContextProperty = 
		newCtx.GetProperty("Interception") as InterceptProperty;

        if (newContextProperty == null)
        {
            Debug.Assert(false);
            return false;
        }

        return true;
    }

    #region IContributeServerContextSink Members

    public IMessageSink GetServerContextSink(IMessageSink nextSink)
    {
        //send calls to our custom sink!!!
        IMessageSink interceptSink = new InterceptSink(nextSink);
        return interceptSink;
    }

    #endregion

    #end region
}

All calls to our application will pass through "HandleMessage" method, there you can log every call, every result, handle exceptions, check authorization for all calls and prevent them when needed, profile methods and every crazy idea or policy you wish to centralize in one place.

C#
public class InterceptSink : IMessageSink
{
    private IMessageSink m_NextSink;
    private static object syncRoot = new object();
     public InterceptSink(IMessageSink nextSink)
    {
        lock (syncRoot)
        {
            m_NextSink = nextSink;
        }
    }
     #region IMessageSink Members
     public IMessage SyncProcessMessage(IMessage msg)
    {
        IMethodReturnMessage returnedMessage;
         HandleMessage(msg, false, out returnedMessage);
         return returnedMessage;
    }
     public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
    {
        IMethodReturnMessage returnedMessage;
         HandleMessage(msg, true, out returnedMessage);
         return m_NextSink.AsyncProcessMessage(msg, replySink);
    }
     public IMessageSink NextSink
    {
        get { return m_NextSink; }
    }
     private void HandleMessage(IMessage msg, bool IsAsync, 
			out IMethodReturnMessage returnedMessage)
    {
        IMethodMessage methodMessage = (IMethodMessage)msg;
        returnedMessage = null;
         if (!IsAsync)
        {
            returnedMessage = (IMethodReturnMessage)m_NextSink.SyncProcessMessage(msg);
             //read exception and do something about it? log?
            exceptionName = GetExceptionName(returnedMessage);
            exceptionMessage = GetExceptionMessage(returnedMessage);
            exceptionTrace = GetExceptionTrace(returnedMessage);
             //profile...?
            TimeSpan exeTimeTimeSpan = DateTime.Now.Subtract(beginTime);
            exeTime = exeTimeTimeSpan.TotalMilliseconds;
        }
        else
        {
            returnedMessage = null;
            exceptionName = "Not Available";
            exceptionMessage = "Method invoked asynchronously";
            exceptionTrace = "Not Available";
        }
    }
     #endregion
}

That's it!

Hope this code will be helpful, do comment if so!!

Good luck.
Diego

Recommended source: Programming .NET Components, Juval Lowy.

This article was originally posted at http://diegworld.blogspot.com/feeds/posts/default?alt=rss

License

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


Written By
Architect Ness Technologies
Israel Israel

Comments and Discussions

 
Questionvery good article Pin
Sudhir Dutt Rawat7-Jan-15 5:48
Sudhir Dutt Rawat7-Jan-15 5:48 
GeneralSynchronization problem with your implementation Pin
DaveBlack7-May-09 5:48
DaveBlack7-May-09 5:48 
GeneralRe: Synchronization problem with your implementation Pin
Diego Resnik7-May-09 8:10
Diego Resnik7-May-09 8:10 
GeneralRe: Synchronization problem with your implementation Pin
Tom Spink4-Jan-10 12:02
Tom Spink4-Jan-10 12:02 
Just out of interest, what race condition do you intend to protect with this lock?

-- Tom Spink, Über Geek.

GeneralRe: Synchronization problem with your implementation Pin
Diego Resnik5-Jan-10 10:32
Diego Resnik5-Jan-10 10:32 
GeneralRe: Synchronization problem with your implementation Pin
Tom Spink5-Jan-10 13:06
Tom Spink5-Jan-10 13:06 
GeneralI found this a good read Pin
CyberJetX22-Apr-09 6:43
CyberJetX22-Apr-09 6:43 
GeneralRe: I found this a good read Pin
Diego Resnik22-Apr-09 9:15
Diego Resnik22-Apr-09 9:15 
Generalmust inherit from ContextBoundObject... Pin
gvdpeer20-Apr-09 23:52
gvdpeer20-Apr-09 23:52 
AnswerRe: must inherit from ContextBoundObject... Pin
Diego Resnik21-Apr-09 0:04
Diego Resnik21-Apr-09 0:04 
GeneralSample code Pin
ntr9h20-Apr-09 16:41
ntr9h20-Apr-09 16:41 
AnswerRe: Sample code Pin
Diego Resnik21-Apr-09 0:08
Diego Resnik21-Apr-09 0:08 

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

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