如何在不手动调用CurrentUnitOfWork.SaveChanges()的情况下使用[UseCase]特性
解决方案
针对ABP框架中使用[UseCase]特性记录实体变更自定义审计原因需要手动调用SaveChangesAsync的问题,有两种低侵入的实现方案:
方案1:自定义工作单元拦截器自动关联原因
[UseCase]需要手动提交的核心原因是默认工作单元会在方法执行完毕后自动提交,而实体变更的原因绑定逻辑默认仅在手动调用SaveChangesAsync时触发,你可以通过新增工作单元拦截器,在自动提交前自动绑定特性中的原因说明:
- 实现自定义拦截器:
public class AutoBindUseCaseReasonInterceptor : IUnitOfWorkInterceptor { public void Intercept(IUnitOfWorkInterceptionContext context) { // 执行原有工作单元逻辑 context.Proceed(); // 读取当前执行方法标注的[UseCase]特性 var useCaseAttribute = context.MethodInvocationTarget.Method .GetCustomAttributes(typeof(UseCaseAttribute), inherit: true) .FirstOrDefault() as UseCaseAttribute; if (useCaseAttribute == null) return; // 绑定原因到本次工作单元产生的所有实体变更记录 if (context.UnitOfWork is IEntityHistoryEnabledUnitOfWork entityHistoryUow) { foreach (var changeSet in entityHistoryUow.EntityHistoryChangeSets) { changeSet.Reason = useCaseAttribute.Description; } } } }
- 在模块的
PreInitialize方法中将拦截器注入到工作单元拦截器集合中即可全局生效,后续所有标注[UseCase]的方法都不需要手动调用SaveChangesAsync,自动提交时会自动写入自定义原因。
方案2:重写实体历史存储逻辑
如果不想修改工作单元的拦截逻辑,可以直接重写默认的IEntityHistoryStore实现:
- 在保存变更记录到数据库前,从当前调用上下文的方法信息中读取
[UseCase]特性的说明,赋值给变更集的Reason字段后再落库 - 该方案侵入性更低,不需要改动工作单元的原有执行逻辑。
注意事项
- 如果同一个工作单元内嵌套调用了多个标注
[UseCase]的方法,建议取最外层方法的特性值作为统一原因,避免出现原因覆盖的问题 - 如果你需要支持动态传入自定义原因(不是写死在特性中的固定值),可以自己实现一个作用域级的
ICurrentUseCaseReason服务,在业务方法中设置原因后,在拦截器/自定义存储逻辑中读取绑定即可,不需要依赖[UseCase]特性的固定配置。
内容的提问来源于stack exchange,提问作者omnomnom
相关产品推荐
相关产品推荐

