Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

AOP using System.Reflection.Emit - Code Injection IL

4.74/5 (22 votes)
2 Jun 20063 min read 1   1.8K  
Very often, we need to intercept method calls and enable some code to run before/after a method call of an external type (.NET object). Learn how to do this.

Introduction

Many times, we need to intercept method calls, and enable some code to run before/after a method call of an external type (.NET object). This article shows how to intercept the methods of an external type, generating method proxies by using injection of IL code.

The dynamic type factory

This factory will generate the dynamic types and return them to the caller. This emulates the external type, and intercepts the specified methods added in the interface. The factory needs the external type and an interface, which establishes the methods that will be intercepted. The sample code here below creates the new type:

C#
public static object Create(object externalTarget, Type interfaceType)
{
    Type proxyType= EmiProxyType(target.GetType(),interfaceType);

    return Activator.CreateInstance(proxyType , 
       new object[]{externalTarget,interfaceType});     
}

After providing the factory with the target object (externalTarget) and the interface (interfaceType which specifies the methods to intercept), we call the method EmiProxyType that builds the method proxy at runtime. Now, the factory can return the enhanced object through reflection.

Defining the interface

As previously said, the interface we pass to the factory specifies which methods we want to intercept; moreover, implemented attributes (AOP) will define the desired behavior.

C#
public interface IBussinesLogicEmployees
{
    [CountingCalls]
     [LoggerToFile]
         EnterpriseDemo.Employees GetEmployees(
             EnterpriseDemo.BussinesLogicEmployees.Delegation delegation);
}

This interface sets the method GetEmployees as interceptable, and defines the attributes CountingCalls and LoggerToFile. If we do not decorate the interface method, the interception code will be empty, so the external method won't execute before/after the additional code.

Defining the attributes

The attributes have tree behaviors inside the method that create the factory:

  1. Before calling to the external method, it must inherit the "BeforeAttribute" class.
  2. After calling to the external method, it must inherit the AfterAttribute class.
  3. If the external method type throws an exception, it must inherit the LogExceptionAttribute class.

By default, implement three attributes:

  1. CountingCalls: this attribute tells the factory to count the number of calls. The action is made before the call to the method of the external type.
  2. LoggerToFile: this attribute tells the factory to generate a log file in which information relative to the access will be stored. The action is made before the call to the external method.
  3. LoggerExceptionToFile: this attribute tells the factory to generate a log file in which information relative to the exception will be stored (in case of the exception being raised by the external method).

Additionally, I have created a custom attribute, ExternalFilter, that will be performed after the execution of the method. It filters the obtained typed dataset according to whether the employee is external or not. The method generated by the factory returns a typed dataset with the external employees, if we decorate the method of the interface with this attribute. In summary, we can extend as many attributes as we want, depending on our needs.

How to intercept an external type method?

To intercept an external type method, we must follow these steps:

  1. Define an interface indicating which are the external type methods to intercept, and what actions the interception code does, depending on the attributes.
  2. Call the factory the following way:
    C#
    IBussinesLogicEmployees iBLExternal = 
        (IBussinesLogicEmployees) CodeInjection.Create(
                new BussinesLogicEmployees(), 
                typeof(IBussinesLogicEmployeesExternalFilter)
                );
  3. Execute the intercepted method:
    C#
    EnterpriseDemo.Employees dsEd =
        iBLExternal.GetEmployees(BussinesLogicEmployees.Delegation.Madrid);

Image 1

In our case, looking at the interface method GetEmployees, notice that it's marked with two attributes, CountingCalls and LoggerToFile, so the factory generated method will register the number of calls and will create a log file with information about the method calls.

Image 2

If, instead, we decorate our interface this way:

C#
public interface IBussinesLogicEmployees {

[LoggerToFile]
[LoggerExceptionToFile]
[ExternalFilter]  
EnterpriseDemo.Employees GetEmployees(
    EnterpriseDemo.BussinesLogicEmployees.Delegation delegation);
}

the factory generated method will create a log file with information about the method call, will create a log file with information about the exception generated by the external type method, will execute the external type method, and finally, will filter the typed dataset returned depending on whether the employees are marked as external or not.

Image 3

Conclusion

In the .NET platform, Aspect Oriented Programming (AOP) can be achieved mainly on two approaches: injecting the IL with the desired code, or alternatively, inheriting the classes from the .NET CLR Library class ContextBoundObject. Injecting the IL code allows interception of methods on external types. So, we can intercept external methods without needing to have the source code of that external type. We use interfaces to help us decide what methods will be intercepted, and decorate interface methods with attributes to run extra actions.

History

  • Release 1.0, 3 June 2006.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here