Aspect Oriented Programming in C#.NET: Part II

Attributed Programming

OK guys, I have never written any article spanning in parts. But the subject demand has forced me to split it into parts. If you are coming to this page directly it is better to first get the AOP basics clear by reading Part I of this article. We will continue with the same problem of the Customer Sales application discussed in Part I. We saw in the previous article, tangling code originated from cross cut concerns. We also found that to solve this problem we separate the cross cut concerns from main concerns.

If you have a close look at the .NET architecture separation of Core Concern and Cross Cut Concerns, it is already implemented using "Attributed Programming". During COM days, if I wanted to serialize a component I had to really code heavy. The serialization code (Cross cut concern) and components business concerns (Core concerns) always tangled. So you move all your cross cut concern code to attributes and your method implements the main concern. In the code below, you can see that the customer class "Add" method core concern is separated from "Serialization" issues of the object using "Attributed Programming".

attributes.jpg

Figure 2.1 Attribute programming in C# way to separate concerns

So is Attribute programming Aspect Oriented Programming? Well again, this is a debate all over. But from my point of view, the answer is "no", attributed programming is one way to separate concerns. But AOP is much above it where we define Joint Point, Point cut and Advice etc. So I will conclude that attributed programming can only separate Concerns which is one of the features of AOP.

OK, so let's try to solve our problem of the previous customer sales application using "Attribute Programming".

Define custom attributes

In this section, we will use custom attributes to implement AOP concern separation. In order that a class be used as an attribute it should inherit from "System.Attribute". So we will inherit both our cross cut concern classes from "System.Attribute".

Lets make a quick glance what steps should be needed to make a class attributed:

  • First the class should inherit from "System.attribute".
    public sealed class ClsEmail : System.Attribute
  • Second, define the attribute usage. Attribute usage tells on what level will this class be attributed, i.e. method level, class Level etc. etc. I leave it to the user to further investigate attribute usage fundamentals. At this moment, for simplicity purpose, we will keep it all.
    [AttributeUsage(AttributeTargets.All)]
  • In order to set the local variables of the class, we need to modify the constructor. For example, in the "ClsEmail" code below, we have a property "strEmail". As this is attribute oriented programming, we can not use "set" and "get". So whatever property we want to set, we will have to define those in the constructor. For example, the "ClsEmail" constructor is modified so that the email property can be passed to it. You can see we have set the email property during the object creation itself. This is the biggest drawback of this approach. The elegance of "Set" and "Get" is lost.
    public ClsEmail(string pstrEmail)
    {
        //
        // TODO: Add constructor logic here
        //
        strEmail = pstrEmail;
        Send();
    }
  • Finally I attribute this to the "ClsCustomer" class on the "Add" method. Note, I am setting the email address using the constructor which looks very shabby.
    [ClsEmail("[email protected]")]
    [ClsPrint("myprinter")]
    public void Add()
    {
        Console.WriteLine("Add routine called of the customer");
    }

OK, so below is the paste of all three codes: i.e. two Cross cut concern (Email and Print) and main concern (Customer). Hope you will be able to follow it.

Cross cut Email code

using System;
using System.Reflection;
namespace CustomerSalesClassProject
{
    /// <summary>
    /// Summary description for ClsEmail.
    /// </summary>

    [AttributeUsage(AttributeTargets.All)]
    public sealed class ClsEmail : System.Attribute
    {
        private string strEmail;
        public ClsEmail(string pstrEmail)
        {
            //
            // TODO: Add constructor logic here
            //
            strEmail = pstrEmail;
            Send();
        }
        public void Send()
        {
            // This method sends email
            Console.WriteLine("Email sent to admin");
            Console.ReadLine();
        }
    }
}

Email class is inherited from "System.Attribute". I will explain in short about the above code. Cross cut concern Print class:

using System;
namespace CustomerSalesClassProject
{
    /// <summary>
    /// Summary description for ClsPrint.
    /// </summary>

    [AttributeUsage(AttributeTargets.All)]
    public class ClsPrint : System.Attribute
    {
        private string strPrinterName;
        public ClsPrint(string pstrPrinterName)
        {
            //
            // TODO: Add constructor logic here
            //
            strPrinterName = pstrPrinterName;
            Print();
        }
        public bool Print()
        {
            // This methods prints the sales or the customer data
            Console.WriteLine("Printed to printer");
            return true;
        }
    }
}

Finally, the customer class which calls both the concerns:

using System;
using System.Reflection;
namespace CustomerSalesClassProject
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>

    public class ClsCustomer : ContextBoundObject
    {
        public ClsCustomer()
        {
            //
            // TODO: Add constructor logic here
            //
        } 

        // Previously tangled code
        /*
        public void Add()
        {
          /////////////////////////////////////////
          // This method adds customer information
          //
          // Adding code will go here
          ////////////////////////////////////////
          // After adding to database email is sent
          ClsEmail pobjClsEmail = new ClsEmail();
          pobjClsEmail.Send();
          // After sending email its printed
          ClsPrint pobjClsPrint = new ClsPrint();
          pobjClsPrint.Print();
        }
        */
        // Attribute applied code
        [ClsEmail("[email protected]")]
        [ClsPrint("myprinter")]
        public void Add()
        {
            Console.WriteLine("Add routine called of the customer");
        }
    }

}

Attributed programming problems

Following are the problems of using attributed programming to implement AOP:

  • The main problem with the above defined way is I can not specify my Advice. In short, I cannot specify at what moment should the Cross cut concern code run. You can see that both the Cross cut concerns are firing before the customer is added to the database. The proper way will be to add the customer to the database and then send email and print it on the printer. We will see in the next section how to define when the Cross cut concern code should be processed, using "Context" attributes.

  • Second, all my property are set using the constructor of the class, which is a very ugly way of doing it. That means all our business logic should run through the constructor also.

  • The last and the main one I have to make changes to is the main core concern class by specifying the attribute on the method. Would not it be best that I just define and code both classes (Cross cut concern) differently and then specify during compiling to weave these two cross cut concerns with the main concern and generate that combined magic code? Hmmm.. looks like a dream but we will be doing that practically in the next section.

The second problem can not really be removed because that's the way .NET attribute programming works. So in order to remove that wait for .NET to either become AOP compliant or live with it.

Context bound objects

First let's try to define what's a context. Context defines an environment which has rules and regulations of how an object will behave. So when an object is bound to a context they are bonded by the rules of the context. Now these rules get applied when the object enters or leaves the context defined environment. If the object is not bound to a context, it's called an Agile object.

Contexts are created when the objects are activated. If there is an existing context then the object is placed into that context or else a new context is created and the object is placed into that.

So for instance I have a context named as "ContextParties" and it has bound objects like "Customer" and "Suppliers". So when either a customer or a supplier object is created, "ContextParties" will be created if not existing and the object will be placed in that context.

OK, now a last fundamental to be cleared regarding context bound objects calling and messaging. If any external client wants to use the context bound object, they can not call them directly, but rather it should use a proxy. So all clients get a reference to the proxy who sends messages to the context and the context communicates with the actual objects. Now while the method messages cross the context, you can intercept these messages and apply some pre-processing or post-processing......Hmmm eureka work around for our first problem defined in attribute programming problems.

Context bound objects are really messy and putting effort to explain how to implement them is not within the scope of this article. If you want to have a know-how of how to do it, you can search in Google. There are plenty of good articles which demonstrate the capability. I do not want you guys to lose stamina in understanding the round about way of implementing AOP, rather the proper way. I am sure after you have read about context bound, you will appreciate that I had dropped it and went ahead to give a real picture of AOP.

Using the EOS compiler

As mentioned before, there is no way we can weave using the current .NET framework. So there is no real time implementation to be seen using the current compiler. I am sure that after one year, this article will look completely stupid, then Microsoft would have made their compilers AOP compatible.

In order to demonstrate the capability of AOP, we will be using the EOS compiler which you can download from here.

So what is EOS guys? I have just pasted it from the website given above.

EOS is an aspect-oriented extension for C# on Microsoft® .NET Framework™. EOS aims to improve upon the current aspect-oriented language model in three dimensions. First, it generalizes aspect instantiation & advice weaving model to eliminate the need for the work-arounds that are unavoidable today when aspects are used to express certain crosscutting concerns. Second, it generalizes the join point model. Third, it aims to eliminate the distinction between class and aspect constructs in favor of a single conceptual building block that combines the expressive capabilities of current classes and aspects, significantly improving conceptual integrity and uniformity in the language design.

So first you have to download the compiler from here. The below sample code is compiled using the 0.3.3 version. You will get a ZIP file with the compiler, documentation etc. Once you unzip it you will see three main folders.

  • Bin (This is where the compiler is present, it's an extension to the C# compiler.)

  • Docs (The documentation.)

  • Examples (Sample code.)

Once you have got all these things, let's code a sample AOP and compile to see the output using the EOS compiler.

Compiling sample code using the EOS compiler

OK guys, after you have done with unzipping and having a look at the various folders, let's try to get some action of how we can use EOP to compile a C# program implementing AOP fundamentals. We will continue with the same sample "Customer" and "Printer" problem which we have described first. Let me revise what the problem was: Customer class updates customer data for the customer. And after the customer data is updated, we would want to print the customer details. So our aspect problem is print, and the main concern is customer data updating.

So we will define three sections:

  • Customer class (Main concern).

  • Printer class (Aspect).

  • Main method to run and see the output.

So first the customer class:

public class ClsCustomer
{
    public ClsCustomer()
    {
        //
        // TODO: Add constructor logic here
        //
    }
    public void Update()
    {
        System.Console.WriteLine("Update Method called");
    }
}

The above customer class does not have much, it has the Update method which does the database operation.

Now the second aspect printer class.... This is rocking guys with new syntaxes inside. I have marked all AOP syntaxes in bold so that you can imagine in future what type of syntaxes can be expected. As printer is my Aspect, I have defined an aspect class called as "ClsCrossCutprinter".

public aspect ClsCrossCutPrinter
{
    public ClsCrossCutPrinter()
    {
        //
        // TODO: Add constructor logic here
        //
    }
}

Now the second thing is the "introduce" keyword, which says to weave this method (Print) in the customer class.

introduce in Customer.ClsCustomer
{
public static void Print()
{
    System.Console.WriteLine("Print cross cut concern called");
    Console.ReadLine();
}
}

And finally my advice saying when it should execute: that is after the Update method is executed.

static after execution (public void
       Customer.ClsCustomer.Update()): call proxyprint();
public void proxyprint()
{
    Customer.ClsCustomer.Print();
}
}

And finally the main static method which will run the whole stuff. Nothing special is happening here: we are creating the customer class and executing the "Add" method.

public class NameSpaceRunningtheProgram
{
    public static void Main(string[] arguments)
    {
        ClsCustomer cust = new ClsCustomer();
        cust.Update();
    }
}

Okay guys, we have not written a single word in the Customer class and the aspect inserts the code inside the class. Now time to compile this class.

So go to the bin directory and run: eos.exe "c:\*.cs".

If everything compiles fine you will see the following output:

Eos Compiler Version 0.3
Copyright (C) The Rector and Visitors of the University of Virginia.
All rights reserved.

OK, now let's try to run the compiled EXE. You will get some error like this:

error.jpg

Figure 2.2 Error, need to copy "Eos.Runtime.dll" to the EXE folder

OK, the error is because you need to copy "Eos.Runtime.dll" where the EXE is present. So just go to the bin directory and copy the file. Now when you run the EXE, you will see the following output.

Update Method called
Print cross cut concern called

The update method is called followed by the Print cross cut method..... without changing the customer class. I have also provided a Zip file on the top of this article which has the complete source code.

OK guys, it's has been a lot big article but hope it must have benefited to the .NET community.


Similar Articles