什么是 aop ?
aop(aspect-oriented programming,面向切面编程)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,以提高代码的模块化性、可维护性和复用性。
在传统的面向对象编程中,我们通常通过类和对象来组织和实现功能。然而,某些功能,如日志记录、事务管理、安全性检查等,可能会跨越多个对象和模块,这种跨越称为横切关注点。aop 的核心思想是将这些横切关注点从业务逻辑中分离出来,通过特定的机制将它们应用到代码中,而不是通过直接修改业务逻辑来实现。
.net core 中 有哪些 aop 框架?
postsharp(收费)
postsharp是一个功能强大的aop框架,它通过编译器插件的形式集成到visual studio中。postsharp支持编译时aop(通过c#特性应用切面),并提供了丰富的切面类型,包括方法拦截、属性访问拦截、异常处理等。它还提供了商业支持和丰富的文档。
castle dynamicproxy
castle dynamicproxy是castle项目的一部分,它允许开发者在运行时动态创建代理类,这些代理类可以拦截对目标对象的调用,并在调用前后执行自定义逻辑。虽然它本身不是一个完整的aop框架,但它经常被用作构建aopkb88凯时官网登录的解决方案的基础。
aspectcore framework
aspectcore 是一个开源的 aop 框架,专为 .net core 设计。它提供了基于动态代理的运行时切面和方法拦截机制,支持常见的切面编程需求,如日志、缓存、事务等。
基于 castle dynamicproxy 实现 aop
1. 安装castle.core nuget包
install-package castle.core
2. 定义接口和类
假设你有一个接口和一个实现了该接口的类,你想要拦截这个类的方法调用。
public interface imyservice
{
void performaction();
}
public class myservice : imyservice
{
public void performaction()
{
console.writeline("action performed.");
}
}
3. 创建拦截器
接下来,你需要创建一个拦截器类,该类将实现iinterceptor
接口。在这个接口的实现中,你可以定义在调用目标方法之前和之后要执行的逻辑。
using castle.dynamicproxy;
public class myinterceptor : iinterceptor
{
public void intercept(iinvocation invocation)
{
// 在调用目标方法之前执行的逻辑
console.writeline("before method: " invocation.method.name);
// 调用目标方法
invocation.proceed();
// 在调用目标方法之后执行的逻辑
console.writeline("after method: " invocation.method.name);
}
}
4. 创建代理并调用方法
最后,你需要使用proxygenerator
类来创建myservice
的代理实例,并指定拦截器。然后,你可以像使用普通对象一样调用代理的方法,但拦截器中的逻辑会在调用发生时执行。
using castle.dynamicproxy;
public class program
{
public static void main(string[] args)
{
var generator = new proxygenerator();
var interceptor = new myinterceptor();
// 创建myservice的代理实例,并指定拦截器
var proxy = generator.createinterfaceproxywithtarget(
new myservice(), interceptor);
// 调用代理的方法,拦截器中的逻辑将被执行
proxy.performaction();
}
}
注意,上面的示例使用了接口代理(createinterfaceproxywithtarget
),这意味着你的目标类必须实现一个或多个接口。如果你想要代理一个类而不是接口,你可以使用createclassproxywithtarget
方法(但这通常用于需要代理非虚方法或字段的场景,且要求目标类是可继承的)。
ioc中使用 castle dynamicproxy
由于ioc容器(如microsoft的iservicecollection
和iserviceprovider
)通常不直接支持aop,所以用 autofac
1. 安装必要的 nuget 包
首先,确保你的项目中安装了以下 nuget 包:
install-package autofac
install-package autofac.extensions.dependencyinjection
install-package autofac.extras.dynamicproxy
install-package castle.core
2. 创建服务接口和实现类
public class user
{
public long id { get; set; }
public string name { get; set; }
public long createuserid { get; set; }
public string createusername { get; set; }
public datetime createtime { get; set; }
public long updateuserid { get; set; }
public string updateusername { get; set; }
public datetime updatetime { get; set; }
}
public interface iuserservice
{
void test();
task tasktest();
void add(user user);
void update(user user);
}
public class userservice : iuserservice
{
public void test()
{
console.writeline("test");
}
public async task tasktest()
{
await console.out.writelineasync("tasktest");
return 1;
}
public void add(user user)
{
console.writeline(user.createuserid);
console.writeline(user.createusername);
}
public void update(user user)
{
console.writeline(user.updateuserid);
console.writeline(user.updateusername);
}
}
[apicontroller]
[route("[controller]")]
public class usercontroller : controllerbase
{
readonly iuserservice _userservice;
public usercontroller(iuserservice userservice)
{
_userservice = userservice;
}
[httpget]
[route("/tasktest")]
public async task tasktest()
{
await _userservice.tasktest();
return "ok";
}
[httpget]
[route("/test")]
public string test()
{
_userservice.test();
return "ok";
}
[httpget]
[route("/add")]
public string add()
{
_userservice.add(new model.user { name = "张三" });
return "ok";
}
[httpget]
[route("/update")]
public string update()
{
_userservice.update(new model.user { name = "张三" });
return "ok";
}
}
3. 创建拦截器类
创建一个实现 iinterceptor
接口的拦截器类 logginginterceptor
,用于拦截方法调用并添加日志记录:
public class logginginterceptor : iinterceptor
{
public void intercept(iinvocation invocation)
{
console.writeline($"before executing: {invocation.method.name}");
invocation.proceed(); // 调用原始方法
console.writeline($"after executing: {invocation.method.name}");
}
}
4. 配置 autofac 容器
builder.host.useserviceproviderfactory(new autofacserviceproviderfactory()) //使用autofac
.configurecontainer(autofacbuilder =>
{
autofacbuilder.registertype();
autofacbuilder.registertype().as ().singleinstance().asimplementedinterfaces()
.enableinterfaceinterceptors() // 启用接口拦截器
.interceptedby(typeof(logginginterceptor)); //指定拦截器
});
与autofac集成时,配置拦截器主要有两种方式
使用 interceptattribute
特性
这种方式通过在接口或类上添加[intercept(typeof(yourinterceptor))]
特性来指定拦截器。然后,在autofac注册时,启用接口或类的拦截器。(通常不推荐在类上直接添加,因为这会使类与autofac紧密耦合)
[intercept(typeof(userautofillinterceptor))]
public class userservice : iuserservice
{
public void test()
{
console.writeline("test");
}
}
autofacbuilder.registertype().as().enableinterfaceinterceptors() // 启用接口拦截器
使用 interceptedby()
方法
这种方式不依赖于[intercept]
特性,而是在注册服务时直接使用interceptedby()
方法来指定拦截器。
autofacbuilder.registertype().as()
.enableinterfaceinterceptors() // 启用接口拦截器
.interceptedby(typeof(logginginterceptor)); //指定拦截器
实现事务管理
拦截器基类
///
/// 拦截基类
///
///
public abstract class baseinterceptor : iinterceptor
{
protected readonly ilogger _logger;
public baseinterceptor(ilogger logger)
{
_logger = logger;
}
///
/// 拦截方法
///
///
public virtual void intercept(iinvocation invocation)
{
try
{
method = invocation.methodinvocationtarget ?? invocation.method;
intercepthandle(invocation);
}
catch (exception ex)
{
_logger.logerror(ex, ex.message);
throw ex;
}
}
///
/// 拦截处理
///
///
public abstract void intercepthandle(iinvocation invocation);
protected methodinfo method{ get; set; }
public static bool isasyncmethod(methodinfo method)
{
return (method.returntype == typeof(task) ||
(method.returntype.isgenerictype && method.returntype.getgenerictypedefinition() == typeof(task<>))
);
}
}
事务特性:用来判断是否需要事务管理的
///
/// 事务特性
///
[attributeusage(attributetargets.method | attributetargets.class, inherited = true)]
public class transactionalattribute : attribute
{
public transactionalattribute()
{
timeout = 60;
}
///
///
///
public int timeout { get; set; }
///
/// 事务隔离级别
///
public isolationlevel isolationlevel { get; set; }
///
/// 事务传播方式
///
public propagation propagation { get; set; }
}
///
/// 事务传播方式
///
public enum propagation
{
///
/// 默认:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中。
///
required = 0,
///
/// 使用当前事务,如果没有当前事务,就抛出异常
///
mandatory = 1,
///
/// 以嵌套事务方式执行
///
nested = 2,
}
事务拦截器:处理事务的
///
/// 事务拦截器
///
public class transactionalinterceptor : baseinterceptor
{
public transactionalinterceptor(ilogger logger) : base(logger)
{
}
public override void intercepthandle(iinvocation invocation)
{
if (method.getcustomattribute(true) == null && method.declaringtype?.getcustomattribute(true) == null)
{
invocation.proceed();
}
else
{
try
{
console.writeline("开启事务");
//执行方法
invocation.proceed();
// 异步获取异常,先执行
if (isasyncmethod(invocation.method))
{
var result = invocation.returnvalue;
if (result is task)
{
task.waitall(result as task);
}
}
console.writeline("提交事务");
}
catch (exception ex)
{
console.writeline("回滚事务");
_logger.logerror(ex, ex.message);
throw ex;
}
}
}
}
接口上加入事务特性
//[transactional]
public class userservice : iuserservice
{
[transactional]
public void test()
{
console.writeline("test");
}
}
注入ioc
builder.host.useserviceproviderfactory(new autofacserviceproviderfactory())
.configurecontainer(autofacbuilder =>
{
autofacbuilder.registertype();
autofacbuilder.registertype().as().singleinstance().asimplementedinterfaces()
.enableinterfaceinterceptors()
.interceptedby(typeof(transactionalinterceptor));
});
测试
实现用户自动填充
上下户用户
public interface ihttpcontextuser
{
long userid { get; }
string username { get;}
}
public class httpcontextuser : ihttpcontextuser
{
private readonly ihttpcontextaccessor _accessor;
public httpcontextuser(ihttpcontextaccessor accessor)
{
_accessor = accessor;
}
public long userid
{
get
{
return 1; //这里暂时是写死的
if (int.tryparse(_accessor.httpcontext?.user?.findfirstvalue(claimtypes.sid), out var userid))
{
return userid;
}
return default;
}
}
public string username
{
get
{
return "admin"; //这里暂时是写死的
return _accessor.httpcontext?.user?.findfirstvalue(claimtypes.name) ?? "";
}
}
}
注入ioc
builder.services.addhttpcontextaccessor();
builder.host.useserviceproviderfactory(new autofacserviceproviderfactory())
.configurecontainer(autofacbuilder =>
{
autofacbuilder.registertype().as().singleinstance().asimplementedinterfaces();
autofacbuilder.registertype();
autofacbuilder.registertype().as().singleinstance().asimplementedinterfaces()
.enableinterfaceinterceptors()
.interceptedby(typeof(userautofillinterceptor));
});
用户自动拦截器:处理用户填充的
///
/// 用户自动填充拦截器
///
public class userautofillinterceptor : baseinterceptor
{
private readonly ihttpcontextuser _user;
public userautofillinterceptor(ilogger logger,ihttpcontextuser user) : base(logger)
{
_user = user;
}
public override void intercepthandle(iinvocation invocation)
{
//对当前方法的特性验证
if (method.name?.tolower() == "add" || method.name?.tolower() == "update")
{
if (invocation.arguments.length == 1 && invocation.arguments[0].gettype().isclass)
{
dynamic argmodel = invocation.arguments[0];
var gettype = argmodel.gettype();
if (method.name?.tolower() == "add")
{
if (gettype.getproperty("createuserid") != null)
{
argmodel.createuserid = _user.userid;
}
if (gettype.getproperty("createusername") != null)
{
argmodel.createusername = _user.username;
}
if (gettype.getproperty("createtime") != null)
{
argmodel.createtime = datetime.now;
}
}
if (gettype.getproperty("updateuserid") != null)
{
argmodel.updateuserid = _user.userid;
}
if (gettype.getproperty("updateusername") != null)
{
argmodel.updateusername = _user.username;
}
if (gettype.getproperty("updatetime") != null)
{
argmodel.updatetime = datetime.now;
}
}
invocation.proceed();
}
else
{
invocation.proceed();
}
}
}
测试