Dora.Interception 1.0(Github地址:可以访问GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段时间了,最近花了点时间将它升级到2.0,主要有如下的改进:
由于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方法时被执行了一次。