AspectF fluent way to put Aspects into your code for separation of concern

Aspects are common features that you write every now and then in
different parts of your project. it can be some specific way of
handling exceptions in your code, or logging method calls, or
timing execution of methods, or retrying some methods and so on. If
you are not doing it using any Aspect Oriented Programming
framework, then you are repeating a lot of similar code throughout
the project, which is making your code hard to maintain. For ex,
say you have a business layer where methods need to be logged,
errors need to be handled in a certain way, execution needs to be
timed, database operations need to be retried and so on. So, you
write code like this:

public bool InsertCustomer(string firstName, string lastName, int age,
    Dictionary<string, string> attributes)
{
    if (string.IsNullOrEmpty(firstName))
        throw new ApplicationException("first name cannot be empty");

    if (string.IsNullOrEmpty(lastName))
        throw new ApplicationException("last name cannot be empty");

    if (age < 0)
        throw new ApplicationException("Age must be non-zero");

    if (null == attributes)
        throw new ApplicationException("Attributes must not be null");

    // Log customer inserts and time the execution
    Logger.Writer.WriteLine("Inserting customer data...");
    DateTime start = DateTime.Now;

    try
    {
        CustomerData data = new CustomerData();
        bool result = data.Insert(firstName, lastName, age, attributes);
        if (result == true)
        {
            Logger.Writer.Write("Successfully inserted customer data in "
                + (DateTime.Now-start).TotalSeconds + " seconds");
        }
        return result;
    }
    catch (Exception x)
    {
        // Try once more, may be it was a network blip or some temporary downtime
        try
        {
            CustomerData data = new CustomerData();
            if (result == true)
            {
                Logger.Writer.Write("Successfully inserted customer data in "
                    + (DateTime.Now-start).TotalSeconds + " seconds");
            }
            return result;
        }
        catch
        {
            // Failed on retry, safe to assume permanent failure.

            // Log the exceptions produced
            Exception current = x;
            int indent = 0;
            while (current != null)
            {
                string message = new string(Enumerable.Repeat('t', indent).ToArray())
                    + current.Message;
                Debug.WriteLine(message);
                Logger.Writer.WriteLine(message);
                current = current.InnerException;
                indent++;
            }
            Debug.WriteLine(x.StackTrace);
            Logger.Writer.WriteLine(x.StackTrace);

            return false;
        }
    }

}

Here you see the two lines of real code, which inserts the
Customer calling a class, is hardly visible due to all the
concerns (log, retry, exception handling, timing)
you have to implement in business layer. There’s validation,
error handling, caching, logging, timing, auditing, retring,
dependency resolving and what not in business layers nowadays. The
more a project matures, the more concerns get into your codebase.
So, you keep copying and pasting boilerplate codes and write the
tiny amount of real stuff somewhere inside that boilerplate.
What’s worse, you have to do this for every business layer
method. Say now you want to add a UpdateCustomer method in
your business layer. you have to copy all the concerns again and
put the two lines of real code somewhere inside that
boilerplate.

Think of a scenario where you have to make a project wide change
on how errors are handled. You have to go through all the hundreds
of business layer functions you wrote and change it one by one. Say
you need to change the way you time execution. You have to go
through hundreds of functions again and do that.

Aspect Oriented Programming solves these challenges. When you
are doing AOP, you do it the cool way:

[EnsureNonNullParameters]
[
Log]
[
TimeExecution]
[
RetryOnceOnFailure] public void InsertCustomerTheCoolway(string firstName, string lastName, int age, Dictionary<string, string> attributes) { CustomerData data = new CustomerData(); data.Insert(firstName, lastName, age, attributes); }

Here you have
separated the common stuffs like logging, timing, retrying,
validation, which are formally called ‘concern’,
completely out of your real code. The method is nice and clean, to
the point. All the concerns are taken out of the code of the
function and added to the function using Attribute. Each
Attribute here represents one Aspect. For example, you can
add Logging aspect to any function just by adding the Log
attribute. Whatever AOP framework you use, the framework ensures
the Aspects are weaved into the code either at build time or at
runtime.

There are AOP frameworks which allows you to weave the Aspects
at compile time by using post build events and IL manipulations eg
PostSharp, some does it at runtime
using
DynamicProxy
and some requires your classes to inherit from
ContextBoundObject in order to
support Aspects
using C# built-in features. All of these have
some barrier to entry, you have to justify using some external
library, do enough performance test to make sure the libraries
scale and so on. What you need is a dead simple way to achieve
“separation of concern”, may not be full blown Aspect
Oriented Programming. Remember, the purpose here is separation of
concern and keep code nice and clean.

So, let me show you a dead simple way of separation of concern,
writing standard C# code, no Attribute or IL manipulation black
magics, simple calls to classes and delegates, yet achieve nice
separation of concern in a reusable and maintainable way. Best of
all, it’s light, just one small class.

public void InsertCustomerTheEasyWay(string firstName, string lastName, int age,
    Dictionary<string, string> attributes)
{
    AspectF.Define
        .Log(Logger.Writer, "Inserting customer the easy way")
        .HowLong(Logger.Writer, "Starting customer insert", 
"Inserted customer in {1} seconds") .Retry() .Do(() => { CustomerData data = new CustomerData(); data.Insert(firstName, lastName, age, attributes); }); }

If you want to read details about how it works and it can save
you hundreds of hours of repeatetive coding, read on:

AspectF
fluent way to add Aspects for cleaner maintainable code

If you like it, please vote for me!

15 thoughts on “AspectF fluent way to put Aspects into your code for separation of concern”

  1. The retry option does not work as it will execute even if the call to the work is done OK.

    From your example:

    AspectF.Define

    .Retry()

    .Do(() =>

    {

    CustomerData data = new CustomerData();

    data.Insert(firstName, lastName, age, attributes);

    }

    );

  2. Glad to help.

    I like this method to the PostSharp way. I do not like post processing of my code, obfuscation and copy protection is more than what I like but have to live with.

    This will work well if you keep the “Aspects” simple like logging, retries ect. If you don't it will make the code more difficult to maintain.

    Very well done!

  3. My future plans for aspects are:

    Cache

    Auto cache method return value based on hash of input parameters or custom defined key.

    Policy

    Integrate with EntLib policy block to execute code when policy satisfies.

    Rule

    Simple rule engine to support running rules against input parameter and execute code when rules satisfy.

    Any other recommendation?

  4. Superb one!!

    I am amazed by the first look on this idea..no doubt you've shown your talent once again!! Brilliant!

    Right now in a hurry- leaving office..but I will think about this again with more concentration 🙂 Just to consider possibilities if how it fits into my works 🙂

    “My future plans for aspects are:

    …..

    Any other recommendation?”

    I guess, you should try to fit the “Transaction management” into this framework. because IMHO, most of the people picks AOP stuffs to maintain transactions rather than Logging, validations etc (at least this is true for JAVA world).

    Once again…Best wishes for this Great Job!!!

  5. Great article.

    I see that you have included it in dropthings.com project.

    when you are planning to release it as a new version of dropthings ?

  6. Omar, that looks great.

    According to my experience with extension methods and delegates/lambda expressions like you have in example (inside method .Do()),

    Can we predict the stack trace of exception if occured?

    I mean, will it be clear to understand in what place it occured?

    Hope you got my thoughts.

    Sincerely yours, Alex

  7. Hi Johannes, would you be interested to join the AspectF development and contribute your enhancement/fixes there? You just need to get a GoogleID and I will make you a “developer” in the project. Then you check directly checkin code in the repository.

    I would love to have the enhancements you made.

  8. Brilliant.. simple, extendable with no performance penalties. Thank you plenty!!

    Using it on a Repository, UnitOfWork dance. Helped separating the UnitOfWork, caching, mapping and other concerns.

    Here is an example of a typical UnitOfWork:

    Aspect.Define

    .RemoveCache(EntityCacheKeys.ForCustomerPreferences(customerID))

    .UnitOfWork(repository=>

    {

    Customer customer = repository.Find(customerID);

    var pref = ..

    repository.Add(pref));

    }

Leave a Reply