65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 System.Reflection.Emit 进行 AOP - 代码注入 IL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (20投票s)

2006年6月2日

3分钟阅读

viewsIcon

98047

downloadIcon

1778

我们经常需要拦截方法调用,并在外部类型(.NET 对象)的方法调用之前/之后执行某些代码。了解如何做到这一点。

引言

许多时候,我们需要拦截方法调用,并在外部类型(.NET 对象)的方法调用之前/之后执行某些代码。本文将介绍如何通过注入 IL 代码生成方法代理,从而拦截外部类型的方​​法。

动态类型工厂

此工厂将生成动态类型并将其返回给调用者。它模拟外部类型,并拦截接口中添加的指定方法。该工厂需要外部类型和接口,接口定义了将被拦截的方法。下面的示例代码创建了新类型

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

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

在向工厂提供目标对象(externalTarget)和接口(interfaceType,它指定要拦截的方法)后,我们调用 EmiProxyType 方法,该方法在运行时构建方法代理。现在,工厂可以通过反射返回增强的对象。

定义接口

如前所述,我们传递给工厂的接口指定了我们要拦截的方法;此外,实现的属性(AOP)将定义所需的行为。

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

此接口将 GetEmployees 方法设置为可拦截,并定义了 CountingCallsLoggerToFile 属性。如果我们不装饰接口方法,拦截代码将为空,因此外部方法不会在附加代码之前/之后执行。

定义属性

这些属性在创建工厂的方法中有三种行为

  1. 在调用外部方法之前,它必须继承“BeforeAttribute”类。
  2. 在调用外部方法之后,它必须继承 AfterAttribute 类。
  3. 如果外部方法类型抛出异常,它必须继承 LogExceptionAttribute 类。

默认实现三个属性

  1. CountingCalls:此属性告诉工厂计算调用次数。该操作在调用外部类型的方法之前执行。
  2. LoggerToFile:此属性告诉工厂生成一个日志文件,其中将存储与访问相关的信息。该操作在调用外部方法之前执行。
  3. LoggerExceptionToFile:此属性告诉工厂生成一个日志文件,其中将存储与异常相关的信息(以防外部方法引发异常)。

此外,我还创建了一个自定义属性 ExternalFilter,它将在方法执行后执行。它根据员工是否为外部员工来过滤获得的类型化数据集。如果使用此属性装饰接口的方法,工厂生成的方法将返回一个包含外部员工的类型化数据集。总之,我们可以根据需要扩展任意数量的属性。

如何拦截外部类型的方法?

要拦截外部类型的方法,我们必须遵循以下步骤

  1. 定义一个接口,指示要拦截的外部类型方法以及拦截代码将执行的操作(取决于属性)。
  2. 按如下方式调用工厂
    IBussinesLogicEmployees iBLExternal = 
        (IBussinesLogicEmployees) CodeInjection.Create(
                new BussinesLogicEmployees(), 
                typeof(IBussinesLogicEmployeesExternalFilter)
                );
  3. 执行被拦截的方法
    EnterpriseDemo.Employees dsEd =
        iBLExternal.GetEmployees(BussinesLogicEmployees.Delegation.Madrid);

在我们的例子中,查看接口方法 GetEmployees,请注意它被标记了两个属性 CountingCallsLoggerToFile,因此工厂生成的方法将记录调用次数并创建一个包含方法调用信息的日志文件。

如果,相反,我们这样装饰我们的接口

public interface IBussinesLogicEmployees {

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

工厂生成的方法将创建一个包含方法调用信息的日志文件,创建一个包含外部类型方法生成的异常信息的日志文件,执行外部类型的方法,最后,根据员工是否被标记为外部员工来过滤返回的类型化数据集。

结论

在 .NET 平台上,面向切面的编程(AOP)主要可以通过两种方法实现:注入具有所需代码的 IL,或者通过继承 .NET CLR 库类 ContextBoundObject。注入 IL 代码允许拦截外部类型的方法。因此,我们可以拦截外部方法,而无需拥有该外部类型的源代码。我们使用接口来帮助我们决定拦截哪些方法,并使用属性装饰接口方法来执行额外的操作。

历史

  • 发布 1.0,2006 年 6 月 3 日。
© . All rights reserved.