如何用EntityFrameworkCore.Triggered定义自定义触发器类型?事务触发器完整教程
【免费下载链接】EntityFrameworkCore.TriggeredTriggers for EFCore. Respond to changes in your DbContext before and after they are committed to the database.项目地址: https://gitcode.com/gh_mirrors/en/EntityFrameworkCore.Triggered
EntityFrameworkCore.Triggered是一款为 EF Core 提供数据库触发器能力的开源库,让你可以在DbContext的数据保存之前或提交之后自动执行自定义逻辑。本文将带你以**事务触发器(Transactional Triggers)**为例,掌握定义自定义触发器类型的完整方法,只需 3 步即可扩展出属于自己的触发时机。
一、快速认识 EntityFrameworkCore.Triggered
想象这样一个场景:每当SaveChanges被调用时,你希望自动给新注册的学生发送欢迎邮件、打上时间戳,或在数据落库后同步缓存。EntityFrameworkCore.Triggered就是为这类"响应式数据处理"而生的。
它默认提供了 3 种内置触发器类型:
| 触发器接口 | 执行时机 |
|---|---|
IBeforeSaveTrigger<TEntity> | SaveChanges 提交之前 |
IAfterSaveTrigger<TEntity> | SaveChanges 成功之后 |
IAfterSaveFailedTrigger<TEntity> | 保存抛出异常之后 |
这 3 种类型能覆盖大多数场景。但当你需要更精细的控制——比如"在数据库事务提交之前做最后一道检查"或"事务回滚之后发送告警"——就需要定义自定义触发器类型了。
二、为什么需要自定义触发器类型?
很多数据库(如 SQL Server)默认会把每次SaveChanges包裹在一个事务里,但触发器的"before/after save"并不等于"before/after commit":
BeforeSave里的修改包含在事务内,回滚会一并撤销;AfterSave里的修改在事务外执行,即便事务失败,这些操作也可能已经发生。
如果你要手动控制事务(BeginTransaction/Commit/Rollback),就需要 4 个新的触发时机:
💡
IBeforeCommitTrigger、IAfterCommitTrigger、IBeforeRollbackTrigger、IAfterRollbackTrigger
官方正是用这套事务触发器扩展包来演示如何定义自定义触发器类型的,代码位于src/EntityFrameworkCore.Triggered.Transactions目录,下面我们就把它拆开看。
三、定义自定义触发器类型的 3 个步骤
步骤 1:定义触发器接口
为每一种触发时机定义一个泛型接口,接收ITriggerContext<TEntity>上下文。以最简单的IBeforeCommitTrigger为例:
public interface IBeforeCommitTrigger<in TEntity> where TEntity : class { void BeforeCommit(ITriggerContext<TEntity> context); }该接口定义在src/EntityFrameworkCore.Triggered.Transactions.Abstractions/IBeforeCommitTrigger.cs。注意两点:
- 接口只依赖抽象层(
EntityFrameworkCore.Triggered.Abstractions),不依赖 EF Core 本身,这样扩展包更轻量、更易维护; - 使用
in TEntity(协变)约束,让实现可以接收基类型的触发器上下文,天然支持继承。
步骤 2:编写 Raise 扩展方法
触发器需要一个"开关"来在合适的时机被调用。官方为ITriggerSession编写了一系列扩展方法(位于src/EntityFrameworkCore.Triggered.Transactions/TriggeredSessionExtensions.cs):
public static void RaiseBeforeCommitTriggers(this ITriggerSession triggerSession) { // 1. 使用"不级联"的发现策略,避免循环触发 var strategy = new NonCascadingTriggerContextDiscoveryStrategy("BeforeCommit"); // 2. 告诉 TriggerSession:去查找 IBeforeCommitTrigger<> 类型的触发器 // 并为每个实体类型创建对应的描述符(Descriptor) ((TriggerSession)triggerSession).RaiseTriggers( typeof(IBeforeCommitTrigger<>), null, strategy, entityType => new BeforeCommitTriggerDescriptor(entityType)); }这里的两个关键组件值得新手记住:
- 发现策略(DiscoveryStrategy):决定如何扫描 DbContext 中待处理的变更。事务触发器使用
NonCascadingTriggerContextDiscoveryStrategy,即不做级联——提交前不应该再引发新一轮变更; - 触发器描述符(TriggerTypeDescriptor):通过反射把"触发器类型 + 实体类型"绑定成一个可快速调用的委托,见
src/EntityFrameworkCore.Triggered.Transactions/Internal/BeforeCommitTriggerDescriptor.cs。
同理,该文件还实现了RaiseAfterCommitTriggers、RaiseBeforeRollbackTriggers、RaiseAfterRollbackTriggers等全部 8 个入口(同步 + 异步各 4 个),命名规律一目了然:
| 扩展方法 | 对应触发器接口 |
|---|---|
RaiseBeforeCommitTriggers() | IBeforeCommitTrigger<T> |
RaiseAfterCommitTriggers() | IAfterCommitTrigger<T> |
RaiseBeforeRollbackTriggers() | IBeforeRollbackTrigger<T> |
RaiseAfterRollbackTriggers() | IAfterRollbackTrigger<T> |
步骤 3:注册并调用你的触发器
使用方只需安装EntityFrameworkCore.Triggered.Transactions包,在配置中启用事务触发器并注册实现类:
services.AddDbContext<ApplicationDbContext>(options => { options.UseTriggers(triggerOptions => { triggerOptions.UseTransactionTriggers(); // 启用事务触发器 triggerOptions.AddTrigger<LogBeforeCommitTrigger>(); // 注册你的实现 }); });在业务代码中手动控制事务并触发对应时机:
using var tx = context.Database.BeginTransaction(); var triggerSession = context.GetService<ITriggerService>().CreateSession(context); try { await context.SaveChangesAsync(); await triggerSession.RaiseBeforeCommitTriggers(); // ✅ 提交前 await tx.CommitAsync(); await triggerSession.RaiseAfterCommitTriggers(); // ✅ 提交后 } catch { await triggerSession.RaiseBeforeRollbackTriggers(); // ✅ 回滚前 await tx.RollbackAsync(); await triggerSession.RaiseAfterRollbackTriggers(); // ✅ 回滚后 throw; }四、事务触发器的完整生命周期
除了按实体触发(BeforeCommit(ITriggerContext<TEntity>)),扩展包还提供了Lifecycle 生命周期触发器(位于src/EntityFrameworkCore.Triggered.Transactions.Abstractions/Lifecycles/),每次触发周期只执行一次,适合做日志汇总、指标上报:
IBeforeCommitStartingTrigger/IBeforeCommitCompletedTriggerIAfterCommitStartingTrigger/IAfterCommitCompletedTrigger
一次完整的事务触发时序如下:
SaveChanges→ 触发 BeforeSave / AfterSave 触发器- 事务提交前 →
BeforeCommitStarting→ 各实体BeforeCommit→BeforeCommitCompleted Commit之后 →AfterCommitStarting→ 各实体AfterCommit→AfterCommitCompleted- 若发生异常,则依次执行
BeforeRollback→ 回滚 →AfterRollback链路
五、新手最佳实践清单
- 🎯接口放 Abstractions,实现放主包:模仿
EntityFrameworkCore.Triggered.Transactions.Abstractions与EntityFrameworkCore.Triggered.Transactions的分离方式,保持扩展包零 EF Core 依赖; - ⚙️善用配置入口:参考
TriggerOptions(src/EntityFrameworkCore.Triggered/TriggerOptions.cs)的模式,为你的自定义触发器提供UseXxxTriggers()风格的链式配置; - 🔄按需选择级联策略:修改实体的触发器用默认的
EntityAndType级联策略;只读型触发器(如事务触发器)用NoCascade,防止无限循环; - ⚡同时提供同步 + 异步版本:事务触发器每个时机都提供
RaiseXxxTriggers与RaiseXxxAsyncTriggers,建议照做以兼容不同调用场景; - 🧪为每个 Raise 方法写集成测试:可参考
test/EntityFrameworkCore.Triggered.Transactions.Tests/TriggeredDbContextTests.cs中的写法——先SaveChanges,再调用RaiseBeforeCommitTriggers(),断言触发器被调用了正确的次数。
六、总结
通过本文,你已经掌握了用EntityFrameworkCore.Triggered定义自定义触发器类型的完整套路,并以事务触发器为例走通了全流程:
- 定义接口:在 Abstractions 层为每种触发时机声明泛型接口;
- 编写 Raise 扩展方法:配合发现策略与触发器描述符,把触发器接入
ITriggerSession; - 注册并调用:用户通过
UseTriggers配置启用,在事务各阶段显式触发。
这套模式完全可以复用到任何你需要的触发时机——无论是"发布前校验"、"审计归档"还是"缓存失效"。动手试试吧,你的 EF Core 项目也可以拥有独一无二的触发器生态!
【免费下载链接】EntityFrameworkCore.TriggeredTriggers for EFCore. Respond to changes in your DbContext before and after they are committed to the database.项目地址: https://gitcode.com/gh_mirrors/en/EntityFrameworkCore.Triggered
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考