Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本

Dora.Interception 1.0(Github地址:可以访问GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段时间了,最近花了点时间将它升级到2.0,主要有如下的改进:

  • 提供了原生的动态代理生成底层框架Dora.DynamicProxy:之前依赖第三方框架Castle实现最底层的代理生成,但是它不支持基于Task的并行编程(也就是说通过它编写的Interceptor无法实现异步执行),所以我采用IL Emit的方式自行实现了这部分的功能,这些底层的功能实现在Dora.DynamicProxy中。
  • 提供了如下两种形式的拦截方案:
    • 基于实例封装:如果消费的类型是一个接口,那么提供的类型为动态生成的代理类,该代理类封装了目标对象。对于每一个动态生成的接口实现成员来说,它会负责执行应用的Interceptor。如果需要调用目标方法,被封装的目标对象的对应方法会被调用。这种拦截方案要求目标类型实现一个接口,接口中定义的所有方法和属性都是可以被拦截的。
    • 基于类型继承:如果目标类型是一个非Sealed类型,一个继承与它的代理类型会被动态生成。如果Interceptor被应用到目标类型的某个虚方法或者属性上,该成员会在生成的代理类中被重写,进而使Interceptor得以执行。这种拦截机制适合非Sealed类型,只有虚方法/属性能够被拦截。
  • 提供了针对属性的拦截支持:之前的版本支持针对方法的拦截,最新版本中提供了针对属性的拦截支持。我们可以选择将Interceptor应用到某个类型的属性上,也可以单独应用到该属性的Get或者Set方法上。

一、对基于Task的并行编程的支持

由于Dora.Interception将Dora.DynamicProxy作为默认的动态代理类型生成框架,所以不在依赖任何第三发框架,因此在编程会变得更加简单,现在我们来做一个简单的演示。在安装了最新版本的NuGet包Dora.Interception之后,我们可以按照 “约定” 的方式来定义如下这么一个简单的Interceptor类型。为了验证针对Task并行编程的支持,我们特意在拦截方法InvokeAsync中Delay了一秒钟。

public class FoobarInterceptor
{
    private InterceptDelegate _next;

    public FoobarInterceptor(InterceptDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(InvocationContext context)
    {
        Console.WriteLine("Interception task starts.");
        await Task.Delay(1000);
        Console.WriteLine("Interception task completes.");
        await _next(context);
    }
}

我将Interceptor和Interceptor的注册特意区分开来,Interceptor的注册默认采用特性标注的形式来实现,为此我们为上面定义的FoobarInterceptor创建一个对应的特性类型FoobarAttribute。如下面的代码片段所示,FoobarAttribute派生于InterceptorAttribute,FoobarInterceptor在重写的Use方法中被构建,在构建过程中可以指定该Interceptor在整个Interceptor Chain的位置(Order)。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] 
public class FoobarAttribute : InterceptorAttribute
{
    public override void Use(IInterceptorChainBuilder builder)
    {
        builder.Use<FoobarInterceptor>(this.Order);
    }
}

接下来我们定义了简单的类型Demo来使用FoobarInterceptor,Demo实现了接口IDemo,FoobarAttribute标注在需要被拦截的方法InvokeAsync上。

public interface IDemo
{
    Task InvokeAsync();
}

public class Demo : IDemo
{
    [Foobar]
    public Task InvokeAsync()
    {
        Console.WriteLine("Target method is invoked.");
        return Task.CompletedTask;
    }
}

由于Dora.Interception实现了与.NET Core的Dependency Injection的无缝集成,所以我们只需要采用我们熟悉的方式来提供服务实例就可以了。如下面的代码片段所示,我们将IDemo和Demo之间的映射关系注册到创建的ServiceCollection上之后,并没有调用BuildeServiceProvider方法,而是调用BuildInterceptableServiceProvider来创建提供服务的ServiceProvider。

class Program
{
    static void Main(string[] args)
    {
        var demo = new ServiceCollection()
                .AddSingleton<IDemo, Demo>() 
                .BuildeInterceptableServiceProvider()
                .GetRequiredService<IDemo>();  
        demo.InvokeAsync();
        Console.WriteLine("Continue...");
        Console.Read();
    }
}

如下所示的是这段代码的执行结果,我们可以看到应用的FoobarInterceptor被正常执行,而且它完全是以异步的方式执行的。

二、基于虚方法的拦截

如果Demo没有实现任何的接口,并且它不是一个Sealed类型,它的虚方法和属性也是可以被拦截的。比如我们将Demo做了如下的改动。

public class Demo
{
    [Foobar]
    public virtual Task InvokeAsync()
    {
        Console.WriteLine("Target method is invoked.");
        return Task.CompletedTask;
    }
}

所有Demo没有了接口实现,所以我们需要对服务注册代码做相应的修改。执行修后的代码,我们依然会得到相同的输出。

class Program
{
    static void Main(string[] args)
    {
        var demo = new ServiceCollection()
                .AddSingleton<Demo, Demo>() 
                .BuildeInterceptableServiceProvider()
                .GetRequiredService<Demo>();  
        demo.InvokeAsync();
        Console.WriteLine("Continue...");
        Console.Read();
    }
}

三、属性也可被拦截

对于上一版本来说,被拦截的成员仅限于普通的方法,最新的版本增加对属性的支持。如果一个Interceptor被直接应用到某个属性上,它实际上会被同时应用到该属性的Get和Set方法上。比如我们在Demo类型上添加一个Value属性,并在上面标准FoobarAttribute。

public class Demo
{
    [Foobar]
    public virtual object Value { get; set; }
}

接下来我们按照如下的方式获取一个Demo对象,并调用其Value属性的Set和Get方法。

class Program
{
    static void Main(string[] args)
    {
        var demo = new ServiceCollection()
                .AddSingleton<Demo, Demo>()
                .BuildInterceptableServiceProvider()
                .GetRequiredService<Demo>();
        Console.WriteLine("Set...");
        demo.Value = new object();
        Console.WriteLine("Get...");
        var value = demo.Value;
        Console.Read();
    }
}

从如下的输出结果可以看出,我们注册到Value属性上的FoobarInterceptor在Get和Set方法被调用的时候都执行了一遍。

如果我们只需要在某个属性的Get或者Set方法上应用某个Interceptor,我们也可以作针对性的标注。在如下的代码片段中,我们将FoobarAttrbute标准到Get方法上。

public class Demo
{
    public virtual object Value { [Foobar] get; set; }
}

再次执行程序,我们会发现FoobarInterceptor仅仅在调用Value属性的Get方法时被执行了一次。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

[ASP.NET Web API]如何Host定义在独立程序集中的Controller

通过《ASP.NET Web API的Controller是如何被创建的?》的介绍我们知道默认ASP.NET Web API在Self Host寄宿模式下用于...

21580
来自专栏恰童鞋骚年

NoSQL初探之人人都爱Redis:(2)Redis API与常用数据类型简介

  首先,不得不说Redis官方提供了众多的API开发包,但是目前Redis官方版本不支持.Net直接进行连接,需要使用一些第三方的开源类库。目前最流行的就是S...

8310
来自专栏王清培的专栏

log4net 自定义Layout日志字段

最近在使用log4net的时候有一个简单的需求,就是自定义个格式化输出符。这个输出符是专门用来帮我记录下业务ID、业务类型的。比如,“businessID:32...

25550
来自专栏Jackson0714

03.如何实现一个遥控器-命令模式

37170
来自专栏大内老A

从数据到代码——通过代码生成机制实现强类型编程[上篇]

我不知道大家对CodeDOM的代码生成机制是否熟悉,但是有一点可以确定:如果你使用过Visual Studio,你就应该体验过它带给我们在编程上的便利。随便列举...

17590
来自专栏王磊的博客

Thread线程的深刻理解和代理方法参数[有图有真相]

在这说的是Thread的基本用法,线程池ThreadPool在这就不说的,以前的blog有写,基本上两个用法都是相同的。基本用法和图,不需要的大鸟请绕行,谢谢!...

36380
来自专栏王磊的博客

entity framework不查数据库修改或排除指定字段集合通用方法

其中DataDBEntities为数据库实体对象,代码如下: 下载地址:http://files.cnblogs.com/stone_w/EFDBHelper....

33150
来自专栏大内老A

ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则

对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象,在不同的使用场景中可能具有不同的验证规则。举个简...

184100
来自专栏老马说编程

(94) 组合式异步编程 / 计算机程序的思维逻辑

前面两节讨论了Java 8中的函数式数据处理,那是对38节到55节介绍的容器类的增强,它可以将对集合数据的多个操作以流水线的方式组合在一起。本节继续讨论Java...

21070
来自专栏angularejs学习篇

.net捕捉全局未处理异常的3种方式

 我们在实际项目开发中,经常会遇到一些不可预见的异常产生,有的异常在程序运行时就对其进行处理(try) 但是,有的程序不需要每一个地方都用try进行处理,那么针...

22330

扫码关注云+社区

领取腾讯云代金券