将UserManager.Create纳入EF Core事务及TransactionScope相关技术疑问
问题1
是否可在所有需事务的数据库操作中,直接用using (var scope = new TransactionScope())替换_db.Database.BeginTransaction()?TransactionScope是否会带来显著开销或不良副作用?
问题2
在上述带重试策略的事务示例中,能否直接用TransactionScope替换_db.Database.BeginTransaction()?
问题3
若调用该方法的代码已创建TransactionScope(而非显式创建IDbContextTransaction),_db.Database.CurrentTransaction能否检测到父事务?若不能,如何检测是否处于父TransactionScope中?或有无更好的方式加入父事务?
解答
针对问题1
可以用TransactionScope替代DbContext.BeginTransaction(),但需注意以下细节:
- 开销差异:仅操作单个SQL Server数据库且连接字符串一致时,事务会保持本地级别,开销和
BeginTransaction()相差无几;但如果涉及多个资源管理器(比如同时操作EF Core和独立的UserManager数据库连接),会触发分布式事务(MSDTC),带来明显额外开销。 - 潜在副作用:
- 异步场景必须指定
TransactionScopeAsyncFlowOption.Enabled,否则会丢失线程上下文,导致事务失效,写法为:using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); - 默认超时1分钟,短于EF Core事务的默认超时,若业务耗时较长需手动设置超时(例如
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled, TimeSpan.FromMinutes(5)))。 - 触发MSDTC时需确保服务器已启用该服务,增加部署复杂度,需提前评估风险。
- 异步场景必须指定
针对问题2
可以替换,但需调整重试逻辑的写法,适配TransactionScope的提交机制,示例代码如下:
public async Task<SomeResultType> AddPatient(<arguments>) { // 同时检查EF显式事务和环境事务 if (_db.Database.CurrentTransaction == null && Transaction.Current == null) { var strategy = _db.Database.CreateExecutionStrategy(); return await strategy.ExecuteAsync(async () => { // 启用异步流的TransactionScope using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); try { var result = await AddPatient(<arguments>); if (result.Success) { // 标记事务完成,否则using结束时自动回滚 scope.Complete(); } return result; } finally { // TransactionScope会在using块结束时自动处理回滚(未调用Complete的情况) } }); } else { // 执行业务逻辑:EF操作 + UserManager.Create() <Do the work> return someResult; } }
关键注意点:
- 需同时检查
Transaction.Current(检测环境事务)和_db.Database.CurrentTransaction(检测EF显式事务),避免重复创建事务。 - 每次重试都要创建新的TransactionScope,因为该对象无法重复使用。
- 若
UserManager.Create()基于EF Core实现且连接字符串一致,会自动加入当前环境事务。
针对问题3
_db.Database.CurrentTransaction无法检测到TransactionScope创建的环境事务,它仅跟踪EF Core自身BeginTransaction方法创建的事务。
- 检测父TransactionScope:通过检查
Transaction.Current != null来判断是否处于环境事务中。 - 加入父事务的最优方式:无需手动干预,只要EF Core操作和
UserManager.Create()使用的资源满足环境事务要求(同一数据库、连接字符串一致),会自动加入现有TransactionScope。 - 额外提醒:若
UserManager使用独立DbContext,需确保其连接字符串与当前_db一致,且未禁用环境事务(EF Core默认启用)。
内容的提问来源于stack exchange,提问作者Bob.at.Indigo.Health
相关产品推荐
相关产品推荐

