Click here to Skip to main content
12,967,445 members (52,834 online)
Click here to Skip to main content
Add your own
alternative version

Stats

7.4K views
56 downloads
10 bookmarked
Posted 17 May 2017

CrossCutterN: A Light Weight AOP Tool for .NET

, 1 Jun 2017 MIT
Rate this:
Please Sign up or sign in to vote.
A brief introduction to CrossCutterN tool for AOP programming in .NET technologies

Introduction

As AOP has become a well known and exercised concept in programming, developers have become more and more dependent on proper AOP tools.

In .NET programming, the best known AOP tool would be PostSharp, which allows injection of custom AOP code using custom attributes. As good things always don't come free, besides some troublesome manual certificate acquiring process, PostSharp express version also has limitations which makes developers who concern about their project scale hesitate to use it, and the price of ultimate version would become a major concern of quite some developers.

To have a free and lightweight AOP tool for .NET, CrossCutterN is introduced. It allows developers to inject custom AOP code into methods/properties of classes via custom attributes and method/property names, and switch on/off injected aspects at runtime. It is totally free, and is able to relieve projects from compile time dependencies on AOP code.

Background

This article assumes that readers are familiar with the concept of Aspect Oriented Programming, and perhaps have some previous experience using AOP frameworks like PostSharp, Spring AOP and so on.

Using the Code

Aspect Injection

Let's take some simple code as an example:

For the following implementation to be built into assembly CrossCutterN.SampleTarget.exe:

namespace CrossCutterN.SampleTarget
{
    using System;
    using SampleAdvice;

    class Target
    {
        public static int Add(int x, int y)
        {
            Console.Out.WriteLine("Add starting");
            var z = x + y;
            Console.Out.WriteLine("Add ending");
            return z;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //CrossCutterN.Advice.Switch.SwitchFacade.Controller.SwitchOn(
            //  "AspectInjectedByAttributeExample");
            Target.Add(1, 2);
        }
    }
}

To output method name and parameter values upon method entry and return value upon method exit for Add function, CrossCutterN tool have 2 ways to achieve the goal.

First of all, some AOP code is to be implemented to output the necessary information. Here for demonstration purpose, a very simple AOP code implementation is provided in assembly CrossCuttern.SampleAdvice.dll:

namespace CrossCutterN.SampleAdvice
{
    using System;
    using System.Text;
    using Advice.Parameter;
    using Advice.Concern;

    public sealed class SampleConcernMethodAttribute : MethodConcernAttribute
    {
    }

    public static class Advices
    {
        public static void InjectByAttributeOnEntry(IExecution execution)
        {
            Console.Out.WriteLine("{0} Injected by attribute on entry: {1}", 
                DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff tt"), GetMethodInfo(execution));
        }

        public static void InjectByAttributeOnExit(IReturn rReturn)
        {
            Console.Out.WriteLine("{0} Injected by attribute on exit: {1}", 
                DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff tt"), GetReturnInfo(rReturn));
        }

        public static void InjectByMethodNameOnEntry(IExecution execution)
        {
            Console.Out.WriteLine("{0} Injected by method name on entry: {1}", 
                DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff tt"), GetMethodInfo(execution));
        }

        public static void InjectByMethodNameOnExit(IReturn rReturn)
        {
            Console.Out.WriteLine("{0} Injected by method name on exit: {1}", 
                DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff tt"), GetReturnInfo(rReturn));
        }

        private static string GetMethodInfo(IExecution execution)
        {
            var strb = new StringBuilder(execution.Name);
            strb.Append("(");
            if (execution.Parameters.Count > 0)
            {
                foreach (var parameter in execution.Parameters)
                {
                    strb.Append(parameter.Name).Append("=").Append(parameter.Value).Append(",");
                }
                strb.Remove(strb.Length - 1, 1);
            }
            strb.Append(")");
            return strb.ToString();
        }

        private static string GetReturnInfo(IReturn rReturn)
        {
            return rReturn.HasReturn ? string.Format("returns {0}", rReturn.Value) : "no return";
        }
    }
}

Note that 4 public static methods are implemented, InjectByAttributeOnEntry and InjectByAttributeOnExit methods will be injected via SampleConcernMethodAttribute defined together; InjectByMethodNameOnEntry and InjectByMethodNameOnExit methods will be injected by the method name "Add".

CrossCutterN tool assumes that AOP code assemblies should reference CrossCutterN.Advice.dll assembly to correctly support the injection behavior. IExecution, IReturn and other necessary and supportive interfaces and implementations are defined in CrossCutterN.Advice.dll assembly. This assembly should be copied over together with AOP code assembly to work together with injected assemblies.

CrossCutterN tool utilizes a console application executable file CrossCutterN.Command.exe to do the AOP code injection. Before executing it, it's configuration file CrossCutterN.Command.exe.config must be properly set up for the AOP code injection process (In sample program it's already prepared in CrossCutterN.Command directory):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="crossCutterN" type="CrossCutterN.Command.Configuration.CrossCutterNSection, CrossCutterN.Command" allowLocation="true" allowDefinition="Everywhere" />
  </configSections>
  <crossCutterN>
    <concernAttributeAspectBuilders>
      <add id="AspectInjectedByAttributeExample">
        <factoryMethod type="CrossCutterN.Aspect.Builder.AspectBuilderFactory, CrossCutterN.Aspect" method="InitializeConcernAttributeAspectBuilder" methodConcernAttribute="CrossCutterN.SampleAdvice.SampleConcernMethodAttribute, CrossCutterN.SampleAdvice"/>
        <pointcut>
          <add joinPoint="Entry" sequence="1" classType="CrossCutterN.SampleAdvice.Advices, CrossCutterN.SampleAdvice" method="InjectByAttributeOnEntry" parameterPattern="Execution"/>
          <add joinPoint="Exit" sequence="2" classType="CrossCutterN.SampleAdvice.Advices, CrossCutterN.SampleAdvice" method="InjectByAttributeOnExit" parameterPattern="Return"/>
        </pointcut>
        <!--<switch status="Off"/>-->
      </add>
    </concernAttributeAspectBuilders>
    <nameExpressionAspectBuilders>
      <add id="AspectInjectedByMethodNameExample">
        <factoryMethod type="CrossCutterN.Aspect.Builder.AspectBuilderFactory, CrossCutterN.Aspect" method="InitializeNameExpressionAspectBuilder">
          <includes>
            <add expression="CrossCutterN.SampleTarget.Target.Ad*"/>
          </includes>
        </factoryMethod>
        <pointcut>
          <add joinPoint="Entry" sequence="2" classType="CrossCutterN.SampleAdvice.Advices, CrossCutterN.SampleAdvice" method="InjectByMethodNameOnEntry" parameterPattern="Execution"/>
          <add joinPoint="Exit" sequence="1" classType="CrossCutterN.SampleAdvice.Advices, CrossCutterN.SampleAdvice" method="InjectByMethodNameOnExit" parameterPattern="Return"/>
        </pointcut>
      </add>
    </nameExpressionAspectBuilders>
  </crossCutterN>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
</configuration>

Please note that in the above configuration:

One aspect builder with id "AspectInjectedByAttributeExample" is added to concernAttributeAspectBuilders collection. According to the value of it's configuration property methodConcernAttribute, any method (property getter and setter as well) marked by the attribute class it's value represents will be injected upon entry and exit with the AOP methods defined in its Pointcut collection. To apply this aspect builder to Add method, apply [SampleConcernMethod] attribute to it defined in CrossCutterN.SampleTarget.Target class.

Similarly, one aspect builder with id "AspectInjectedByMethodNameExample" is added to nameExpressionAspectBuilders collection, according to the setting of which, any method whose full name matches "CrossCutterN.SampleTarget.Target.Ad*" will be injected upon entry and exit with the AOP methods defined in its Pointcut collection.

After all above is done, build the solution in the demo using release configurartion (Of course Debug configuration also works). In Windows command prompt, navigate to CrossCutterN/CrossCutterN.Command folder and then please execute the following commands:

copy ..\CrossCutterN.SampleAdvice\bin\Release\CrossCutterN.SampleAdvice.dll .
copy ..\CrossCutterN.SampleTarget\bin\Release\CrossCutterN.SampleTarget.exe .
copy ..\CrossCutterN.SampleTarget\bin\Release\CrossCutterN.SampleTarget.pdb .
CrossCutterN.Command.exe CrossCutterN.SampleTarget.exe CrossCutterN.SampleTarget_Weaved.exe Y

Note that since we are injecting code implemented in CrossCutterN.SampleAdvice.dll, this assembly must be available in CrossCutterN.Command.exe's search path, most conveniently just copy it to the same directory. The last command tells CrossCutterN.Command.exe command to load CrossCutterN.SampleTarget.exe assembly, inject it with the AOP code according to the content in CrossCutterN.Command.exe.config file, and output the result as assembly CrossCutterN.SampleTarget_Weaved.exe. The "Y" parameter means handling pdb file as well, this parameter is optional, and is only applicable when the target pdb file is within the same folder of the assembly being injected, and very useful if the injected assembly needs to be debugged. Note that an injection log file named like "Wave_yyyy_MM_dd_HH_mm_ss_fff.log" should be generated in the same folder which contains the injection summary.

After the last command is successfully executed, try to execute CrossCutterN.SampleTarget.exe and CrossCutterN.SampleTarget_Weaved.exe, the difference suggests that AOP code has been injected successfully.

Injection by attribute is designed for development on going projects to allow developers to separate AOP code from business code, and easily apply aspects by attributes.

Injection by method name is more designed for built assemblies of which recompile actions are limited or impossible.

Aspect Switching

To provide operational convenience (e.g. trouble shooting), In case that occationaly some injected aspects need to be turned off for a while and later turned on after problems are solved or when necessary, CrossCutterN provides runtime aspect switching feature.

Note the commented out line in CrossCutterN.Command.exe.config:

<!--<switch status="Off"/>-->

Uncomment it, execute the last command (this time it's not necessary to copy over the dll and exe and pdb files if they are there already) to generate CrossCutterN.SampleTarget_Weaved.exe file using the updated configuration. After that execute CrossCutterN.SampleTarget_Weaved.exe, the result suggests that injected feature by aspect builder with id "AspectInjectedByAttributeExample" is not applied anymore.

To switch it on at run time, note the commented out lines in Main method of assembly CrossCutterN.SampleTarget:

//CrossCutterN.Advice.Switch.SwitchFacade.Controller.SwitchOn(
//  "AspectInjectedByAttributeExample");

Uncomment it, rebuild the project, execute the commands again (No need to copy over CrossCutterN.SampleAdvice.dll if it's there already, it is not updated), and execute CrossCutterN.SampleTarget_Weaved.exe, the result suggests that the injected feature by aspect builder with id "AspectInjectedByAttributeExample" is switched on.

There are 3 available options for status configuration property of switch element: 

  • On: The aspect injected by this aspect builder is switchable, turned on by default.
  • Off: The aspect injected by this aspect builder is switchable, turned off by default.
  • NotSwitchable: The aspect injected by this aspect builder is not switchable, will always be executed, can't be turned off. This is the default value if the element is not set in configuration file.

More Details

The above is just a simple demonstration of CrossCutterN tool.

For aspect injection, it can inject methods and properties at points of entry, exception and exit with various of configuration options to easily include or exclude injection target by method/property/constructor, accessibility and static/instance. Multiple aspects can be ordered for each join point (entry, exception and exit), and work together well

For aspect switching, it allows various granularities, like switching all aspects from an aspect builder, all aspects applied to a class, a method, and so on. 

CrossCutterN is much more flexible, configurable and extendable than the introduction above. Interested readers, please visit GitHub to download the source code and find out more details about CrossCutterN.

Considering this tool is still an evolving new born, it's documentation is most likely not perfect, plus though test cases are written and passed, there might still be defects. If there are any issues found during usage of the tool, or if there are any questions, suggestions and requriements, please don't hesitate to leave a comment to this article, submit an issue to CrossCutterN project, or send and email to [email protected].

Attentions

  • Please don't use this tool to inject the already injected assemblies. Take the assembly CrossCutterN.SampleTarget_Weaved.exe mentioned above forexample, if this assembly is injected again using CrossCutterN tool, there is no guarantee that it still works perfectly. 
  • There is no guarantee that CrossCutterN works with any other AOP tools.
  • There is no point to do this AOP code injection process using multi-thread style, for developers tend to develop their own tools based on CrossCutterN source code, please be reminded that the AOP code injection part isn't designed for multi-threading at all (why would someone want 2 thread to inject one assembly).
  • Multi-threading is considered and implemented for aspect switching feature.
  • There is no guarantee that CrossCutterN works with obfuscation tools.

Points of Interest

CrossCutterN depends on Mono.Cecil for IL weaving. For an IL beginner to quickly understand how to write IL code, writing some simple code in C# and opening the compiled assembly using ILdasm.exe is the fastest way.

For aspect switching feature, in case users worry about what the behavior should be if some injected classes are not loaded when switching happens, the asnwer is:

CrossCutterN will record the switching statuses in case some injected classes haven't been loaded into program, and apply the switching history once the class is loaded. This switching status recording process is optimized so that not each and every switching operation is kept in memory before loading the class, but only the minimum switching statuses are recorded to make sure if loading classes after some switching operation, it behaves the same as if the classes have been loaded before any aspect switching happens.

History

  • Initial version
  • Aspect switching feature added

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

David_Cui
Singapore Singapore
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionAbout the link Pin
Nelek31-May-17 18:50
protectorNelek31-May-17 18:50 
AnswerRe: About the link Pin
David_Cui31-May-17 20:10
memberDavid_Cui31-May-17 20:10 
GeneralRe: About the link Pin
Nelek31-May-17 20:11
protectorNelek31-May-17 20:11 
Questiona few questions Pin
BillWoodruff18-May-17 20:23
mvpBillWoodruff18-May-17 20:23 
AnswerRe: a few questions Pin
CuiZiqiang19-May-17 5:50
memberCuiZiqiang19-May-17 5:50 
GeneralRe: a few questions Pin
BillWoodruff21-May-17 13:18
mvpBillWoodruff21-May-17 13:18 
GeneralRe: a few questions Pin
CuiZiqiang21-May-17 14:45
memberCuiZiqiang21-May-17 14:45 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170525.1 | Last Updated 1 Jun 2017
Article Copyright 2017 by David_Cui
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid