软件编程
位置:首页>> 软件编程>> C#编程>> C# 使用 Castle 实现 AOP及如何用 Autofac 集成 Castle

C# 使用 Castle 实现 AOP及如何用 Autofac 集成 Castle

作者:丹枫无迹  发布时间:2023-07-08 16:02:26 

标签:C#,Castle,AOP,autofac
目录
  • 基本用法

    • 基于接口的 *

    • 基于类的 *

    • 异步函数拦截

  • Autofac 集成

    • 基于接口的 *

    • 基于类的 *

    • 异步函数拦截

Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架。发展到现在已经有四个组件:

  • ORM组件:ActiveRecord

  • IOC组件:Windsor

  • * 组件:DynamicProxy

  • Web MVC组件:MonoRail

本文主要介绍 * 组件 Castle.DynamicProxy

基本用法

Castle.DynamicProxy 是通过 Emit 反射动态生成代理类来实现的,效率相对静态植入要慢一点,但比普通的反射又高一些。 * 只对公共接口方法、类中的虚方法生效,因为只有接口中的方法、类中的虚方法才可以在子类中重写。

基于接口的 *


public interface IProductRepository
{
void Add(string name);
}

public class ProductRepository : IProductRepository
{
public void Add(string name) => Console.WriteLine($"新增产品:{name}");
}

public class LoggerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
 var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 执行前");

//调用业务方法
 invocation.Proceed();

Console.WriteLine($"{methodName} 执行完毕");
}
}

class Program
{
static void Main(string[] args)
{
 ProxyGenerator generator = new ProxyGenerator();

IInterceptor loggerIntercept = new LoggerInterceptor();

IProductRepository productRepo = new ProductRepository();

IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);

proxy.Add("大米");

Console.Read();
}
}

基于类的 *


public class ProductRepository
{
public virtual void Add(string name) => Console.WriteLine($"新增产品:{name}");
}

static void Main(string[] args)
{
ProxyGenerator generator = new ProxyGenerator();

IInterceptor loggerIntercept = new LoggerInterceptor();

ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept);
// 使用 CreateClassProxy 泛型方法可以省去实例化代码
//ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);

proxy.Add("大米");
}

在上例中,如果 ProductRepository.Add 不是虚方法,也不会报错,但是 * 不会被调用。

异步函数拦截

Castle.DynamicProxy 对异步函数的拦截跟同步没啥差别,只是,如果要在方法执行完成后插入内容,需要 await


public class ProductRepository
{
public virtual Task Add(string name)
{
 return Task.Run(() =>
     {
      Thread.Sleep(1000);
      Console.WriteLine($"异步新增产品:{name}");
     });
}
}

public class LoggerInterceptor : IInterceptor
{
public async void Intercept(IInvocation invocation)
{
 var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 执行前");

invocation.Proceed();

// 不 await 的话将会先输出“执行完毕”,再输出“异步新增产品”
 var task = (Task)invocation.ReturnValue;
 await task;

Console.WriteLine($"{methodName} 执行完毕");
}
}

上面这个写法是简单粗暴的,如果碰到返回值是 Task<TResult>,或者不是异步函数,就会出错。所以这里是要对返回值进行一个判断的。

可以使用 Castle.Core.AsyncInterceptor 包,它包装了 Castle,使异步调用更简单。

Castle.Core.AsyncInterceptor 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor


public class ProductRepository : IProductRepository
{
public Task Add(string name)
{
 return Task.Run(() =>
     {
      Thread.Sleep(1000);
      Console.WriteLine($"异步新增产品:{name}");
     });
}

public Task<string> Get()
{
 return Task.Run(() =>
     {
      Thread.Sleep(1000);
      Console.WriteLine($"获取产品");

return "大米";
     });
}
}

public class LoggerInterceptor : IAsyncInterceptor
{
public void InterceptAsynchronous(IInvocation invocation)
{
 invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
}

async Task InternalInterceptAsynchronous(IInvocation invocation)
{
 var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 异步执行前");

invocation.Proceed();
 await (Task)invocation.ReturnValue;

Console.WriteLine($"{methodName} 异步执行完毕");
}

public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
 invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);

Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id);
}

private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
{
 var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 异步执行前");

invocation.Proceed();
 var task = (Task<TResult>)invocation.ReturnValue;
 TResult result = await task;

Console.WriteLine(task.Id);

Console.WriteLine($"{methodName} 异步执行完毕");

return result;
}

public void InterceptSynchronous(IInvocation invocation)
{
 var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 同步执行前");

invocation.Proceed();

Console.WriteLine($"{methodName} 同步执行完毕");
}
}

class Program
{
static void Main(string[] args)
{
 ProxyGenerator generator = new ProxyGenerator();

IAsyncInterceptor loggerIntercept = new LoggerInterceptor();

IProductRepository productRepo = new ProductRepository();

IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);

proxy.Get();
}
}

这是 Castle.Core.AsyncInterceptor 提供的示例写法,这里有个问题,也是我的疑惑。invocation.ReturnValue = InternalInterceptAsynchronous(invocation); 将导致代理返回的 Task 是一个新的 Task,这一点我们可以输出 Task.Id 来确认。个人感觉有点画蛇添足。


public async void InterceptAsynchronous<TResult>(IInvocation invocation)
{
var methodName = invocation.Method.Name;

Console.WriteLine($"{methodName} 异步执行前");

invocation.Proceed();
var task = (Task<TResult>)invocation.ReturnValue;
await task;

Console.WriteLine($"{methodName} 异步执行完毕");
}

这样就挺好的。

如果有小伙伴知道为什么要返回一个新的 Task,请留言告诉我,谢谢!

Autofac 集成

Autofac.Extras.DynamicProxy 是一个 Autofac 扩展,可与 Castle 一起提供 AOP 拦截。

基于接口的 *


static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注册 *
builder.RegisterType<LoggerInterceptor>().AsSelf();

//注册要拦截的服务
builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
 .EnableInterfaceInterceptors()     //启用接口拦截
 .InterceptedBy(typeof(LoggerInterceptor));  //指定 *

IContainer container = builder.Build();

IProductRepository productRepo = container.Resolve<IProductRepository>();

productRepo.Add("大米");
}

基于类的 *


static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注册 *
builder.RegisterType<LoggerInterceptor>().AsSelf();

//注册要拦截的服务
builder.RegisterType<ProductRepository>()
 .EnableClassInterceptors()      //启用类拦截
 .InterceptedBy(typeof(LoggerInterceptor));  //指定 *

IContainer container = builder.Build();

ProductRepository productRepo = container.Resolve<ProductRepository>();

productRepo.Add("大米");
}

异步函数拦截

Castle.Core.AsyncInterceptor 中,IAsyncInterceptor 接口并不集成 IInterceptor 接口,而 Autofac.Extras.DynamicProxy 是绑定 Castle 的,所以按上面同步拦截的写法是会报错的。

IAsyncInterceptor 提供了 ToInterceptor() 扩展方法来进行类型转换。


public class LoggerInterceptor : IInterceptor
{
readonly LoggerAsyncInterceptor interceptor;

public LoggerInterceptor(LoggerAsyncInterceptor interceptor)
{
 this.interceptor = interceptor;
}

public void Intercept(IInvocation invocation)
{
 this.interceptor.ToInterceptor().Intercept(invocation);
}
}

public class LoggerAsyncInterceptor : IAsyncInterceptor
{
public void InterceptAsynchronous(IInvocation invocation)
{
 //...
}

public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
 //...
}

public void InterceptSynchronous(IInvocation invocation)
{
 //...
}
}

static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注册 *
builder.RegisterType<LoggerInterceptor>().AsSelf();
builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();

//注册要拦截的服务
builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
 .EnableInterfaceInterceptors()     //启用接口拦截
 .InterceptedBy(typeof(LoggerInterceptor));  //指定 *

var container = builder.Build();

IProductRepository productRepo = container.Resolve<IProductRepository>();

productRepo.Get();
}

来源:https://www.cnblogs.com/gl1573/p/14428321.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com