Click here to Skip to main content
Click here to Skip to main content
Go to top

AOP Using Spring.NET - Part 2

, 12 Jun 2008
Rate this:
Please Sign up or sign in to vote.
Part 2 of AOP and Spring.NET.

Introduction

In my previous article, I discussed about Aspect Oriented Programming and how we can use Spring.NET to implement AOP concepts in a multi-layered application.

In this article, I will enhance the Advice that was created in the earlier article, adding more flexibility which can be used in a real time application.

Limitations of the earlier implementation

Why do we need this part 2 in the same series? Well, there are some limitations in my earlier article and implementation. The gist of my earlier article was to introduce AOP concepts and explain how we can do a simple implementation, to help developers who are new to Spring.NET and AOP. I intentionally made it simple.

The limitations are:

  • The Advice always writes to a log file, one cannot turn on or off.
  • The file name path is hardcoded.

We need greater flexibility for Advices. We should be able to pass arguments to it, so that we can avoid hard-coding and allow flexibility. We should allow developers to create custom Advices to suit their business needs, yet show them a standard way of implementing. To overcome all these limitations, I have come up with this part, in which I have explained how we can implement Advices, putting the best OO concepts and design principles.

Dive deep into creating custom Advices

Application architecture

The application has three logical layers - UI, Business Logic, and Data Access. The Advice is applied to the Data Access Layer. Advices are created as a separate C# class library project. The following diagram gives the logical architecture of the application.

The code uses the Northwind database, the scripts can be downloaded from here.

LogicalArchitecture.JPG

Solution structure

The following diagram gives the solution structure of this application. In the interest of real estate, I have expanded only the AOP project and the rest of the projects are of similar structure like in my previous AOP article.

SolutionStructure.JPG

Detailed design

This section is the meat of the article. I will focus on creating the Advices with more flexibility. Throughout the article, I will be focusing on an Around Advice. I will leave it to you for other types of Advices.

IAroundInterface & AroundAdviceBase

I have created my custom interface called "IAroundAdvice". This will internally inherit from the IMethodInterceptor (Spring.NET) interface. My custom interface defines methods like Initialize, BeforeMethodCall, and AfterMethodCall. The method names are self explanatory. This interface has a getter and setter property called Argument. This interface will also inherit IDisposable. If you need to create an Around advice, you need to implement this interface.

I have created an abstract base class called "AroundAdviceBase" that implements the IAroundAdvice interface. This class provides the default functionality for Around advices.

The AroundAdviceBase class declares the Initialize, BeforeMethodCall, and AfterMethodCall (methods of IAroundAdvice) as abstract methods. The Argument property (property of IAroundAdvice) is declared as an abstract property. The expectation is, the derived class will provide the actual implementation and this class will provide the structure.

The AroundAdviceBase class has an overloaded constructor, in which it calls the Initialize() method.

The AroundAdviceBase class provides the implementation of the IMethodInterceptor.Invoke() method. Inside the method, it calls the abstract methods - BeforeMethodCall(), AfterMethodCall(). The following code snippet shows the Invoke method implementation of the AroundAdviceBase class. The invocation.Proceed() statement calls the actual target method.

public virtual object Invoke(IMethodInvocation invocation)
{
    // Call Before method call activity
    BeforeMethodCall(invocation);

    // Call actual method
    object returnValue = invocation.Proceed();

    // Call After method call activity
    AfterMethodCall(invocation);

    return returnValue;
}

Instead of you implementing IAroundAdvice and implementing all the methods, you can derive from the AroundAdviceBase class and implement only the three abstract methods and one abstract property.

Creating the Around Advice

Once we have the interface and an abstract class defined, it's now time to create a concrete implementation of an Around advice. We want to create an Advice that would write trace messages before and after a method call. In our case, we need to derive from the AroundAdviceBase class, and implement the abstract methods and property. The "TraceAdviceBase" class does this for us.

The TraceAdviceBase class provides an implementation for the Initialize, BeforeMethodCall, and AfterMethodCall methods and the Argument property.

The Initialize() method accepts an object as the argument, and sets the value to an instance of type "TraceAdviceArgument". I will shortly discuss about the type "TraceAdviceArgument". But for now, you can assume that the object is of type "TraceAdviceArgument" and the value is set in the Initialize() method. Initialize() creates a TextWriteTraceListener (System.Diagnostics), creates a file stream object, and adds the same to the trace listener. The name of the file is passed through "TraceAdviceArgument". The method finally initializes a bool variable to indicate whether the TraceAdviceBase has been initialized or not. The following code snippet shows the implementation of the Initialize() method.

public override bool Initialize(object argument)
{

    if (argument != null)
    {
        _traceArgument = (TraceAdviceArgument)argument;
        _stream = File.Create(_traceArgument.TraceName);
        _listener = new TextWriterTraceListener(_stream);
        Trace.Listeners.Add(_listener);

        _isInitialized = true;
    }
    else
    {
        _isInitialized = false;
    }
    return true;
}

The BeforeMethodCall() method checks for the initialized status and writes a "Begin" message to the trace listener that has been created in the Initialize() method.

public override bool BeforeMethodCall(IMethodInvocation argument)
{
    if ( (_isInitialized == true ) && ( argument != null))
    {
        Trace.WriteLine("Begin method call " + argument.Method.Name);
        Trace.Flush();
        return true;
    }
    return false;
}

The AfterMethodCall() method checks for the initialized status and writes a "Completed" message to the trace listener.

public override bool AfterMethodCall(IMethodInvocation argument)
{
    if ((_isInitialized == true) && (argument != null))
    {
        Trace.WriteLine("Completed method call " + argument.Method.Name);
        Trace.Flush();
        return true;
    }
    return false;
}

The Argument property setter will type cast the value to the "TraceAdviceArgument" type and call Initialize(). The implementation is as shown below:

public override object Argument
{
    get { return _traceArgument; }
    set
    {
        _traceArgument = (TraceAdviceArgument)value;
        Initialize(_traceArgument);
    }
}

Passing arguments

Having seen "TraceAdviceArgument" many times, it's now time to look at this class. As the name suggests, the purpose of this class is to pass arguments to the Advice through the configuration. The class defines public get/set properties. If you wish to pass multiple parameters to an Advice, you can wrap all such properties into a custom class, provide public get/set properties, and configure it in the Spring object graph. Spring will create an instance of this class and pass it to Advices. The argument class implementation is given below.

public sealed class TraceAdviceArgument : IDisposable
{
    # region Private Members
    private string _traceName;
    # endregion

    # region Public Constructor
    public TraceAdviceArgument()
    {
        _traceName = string.Empty;
    }

    public TraceAdviceArgument(string traceName)
    {
        _traceName = traceName;
    }
    # endregion

    # region Public Property
    public string TraceName
    {
        get { return _traceName; }
        set { _traceName = value; }
    }
    # endregion

    #region IDisposable Members

    public void Dispose()
    {
        _traceName = null;
        return;
    }

    #endregion
}

I have defined only one argument in this case, it is the file name. You can extend this functionality by adding another property called enabled/disabled which will determine whether to write to trace or not and pass a boolean value from the configuration.

AOP class diagram

The detailed class diagram for the entire AOP project is shown below:

AOPClassDiagram.JPG

Spring configuration

As we all know, everything is hooked at runtime and Spring.NET needs a configuration for the same. The following diagram gives the configuration of the Advice, Argument, and they have been applied to the Customer DAL class.

Configuration.JPG

The object "CustomerDALWithAdvice" points to the CustomerDAL class in the DAL assembly. It has an interceptor, TraceAroundAdvice. This is the Advice that we have created in the previous section.

The "TraceAroundAdvice" object is defined with the type "TraceAdviceBase" (Advice class that we created), and the property named "Argument" points to the "TraceAdviceArgument" class. The TraceAdviceArgument class takes a property called "TraceName" and we have set "C:\Temp\Trace.Log".

So, when you use "CustomerDALWithAdvice", the "TraceAroundAdvice" will be applied, which takes "TraceAdviceArgument" as an instance to the "Argument" property. The argument class in turn defines a property called "TraceName" which represents the physical file name.

Extending the AOP project

So far, I have discussed about the default implementation - "TraceAdviceBase". This takes an argument from the configuration and writes trace messages into a file.

If you need to create an Around Advice that needs to perform some other task like, trace messages to DB, monitor performance statistics (time taken) etc., all you need is to derive from AroundAdviceBase and implement the abstract methods and property.

Conclusion

This article gives detailed info about creating Advices and passing arguments. It introduces you to how you can provide a robust and flexible component that lets developers use the default functionality, or create new functionality and plug it into the system without writing too much code, without breaking the flow and design, and by following design standards set by the component.

Happy reading! Happy coding!

License

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

Share

About the Author

Niranjan Kumar
Architect Cognizant Technology Solutions
United States United States
Solution Architect working for Cognizant Technology Solutions.

Comments and Discussions

 
GeneralMy vote of 5 PinmvpKanasz Robert28-Sep-12 7:14 
QuestionHow this will work for parameterised contructor class ? Pinmembervikram30capri9-Jul-10 0:25 
QuestionWhich version of Spring.net did this sample code use? PinmemberSmith200021-Sep-09 6:48 
GeneralCalling to be traced claases with constructor PinmemberMember 8117062-Nov-08 23:44 

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
Web03 | 2.8.140921.1 | Last Updated 12 Jun 2008
Article Copyright 2008 by Niranjan Kumar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid