entity framework6异步操作和TranscationScope

我在stackoverflow上搜索但找不到类似的问题,请指出我是否已经有一个问题。

我试图用同步和异步操作实现一个通用的可重用存储库,但是我对entity framework和工作单元的了解很少,我很难找到实现它的正确方法。

我在SaveAndCommit操作中添加了一些变体,但不知道使用事务和异步执行此操作的最佳方法是什么。

– – 编辑 – –

根据我的理解,当执行多个操作时应该使用事务,但出于理解的目的,我将它用于一个操作。 (如果我错了,请纠正我)

这是我到目前为止所做的

public class Service : IService where TEntity : Entity { #region Constructor and Properties UnitOfWork _unitOfWork { get { return UnitOfWork.UnitOfWorkPerHttpRequest; } } protected DbSet Entities { get { return _unitOfWork.Set(); } } #endregion Constructor and Properties #region Operations public virtual IQueryable QueryableEntities() { return Entities; } public virtual async Task<IList> WhereAsync(Expression<Func> predicate) { return await Entities.Where(predicate).ToListAsync(); } public virtual IList Where(Expression<Func> predicate) { return Entities.Where(predicate).ToList(); } public virtual async Task FirstOrDefaultAsync(Expression<Func> predicate) { return await Entities.FirstOrDefaultAsync(predicate); } public virtual TEntity FirstOrDefault(Expression<Func> predicate) { return Entities.FirstOrDefault(predicate); } public virtual async Task GetByIdAsync(int id) { return await Entities.FindAsync(id); } public virtual TEntity GetById(int id) { return Entities.Find(id); } // Method to the change the EntityState public virtual void Save(TEntity entity) { if (entity.Id == 0) { Entities.Add(entity); } else { _unitOfWork.Entry(entity).State = EntityState.Modified; } } #region Need clarification here // Uses transaction scope to commit the entity and dispose automatically // call rollback but this is not async and don't have any async // functions (Or I could not find) public virtual void SaveAndCommit(TEntity entity) { using (var transaction = _unitOfWork.BeginTransaction()) { try { Save(entity); transaction.Commit(); } catch (DbEntityValidationException e) { } } } // This is asynchronous but don't uses transaction public virtual async Task SaveAndCommitAsync(TEntity entity) { try { Save(entity); await _unitOfWork.SaveChangesAsync(); } catch (DbEntityValidationException e) { } } // Tried to mix async and transaction but don't know if it will actually // work or correct way of doing this public virtual async Task SaveAndCommitWithTransactionAsync(TEntity entity) { using (var transaction = _unitOfWork.BeginTransaction()) { try { Save(entity); await _unitOfWork.SaveChangesAsync(); } catch (DbEntityValidationException e) { transaction.Rollback(); } } } #endregion Need clarification here public virtual async Task DeleteAsync(TEntity entity) { if (entity == null) return; Entities.Remove(entity); await _unitOfWork.SaveChangesAsync(); } //All similar methods for delete as for Save public virtual async Task CountAsync(Expression<Func> predicate = null) { if (predicate != null) { return await Entities.CountAsync(predicate); } return await Entities.CountAsync(); } #endregion Operations } 

请指导我并建议实现这一目标的最佳方法。


现在看来,使用异步调用实现事务范围的正确方法是

 public virtual async Task SaveAndCommitWithTransactionAsync(TEntity entity) { using (var transaction = _unitOfWork.BeginTransaction()) { Save(entity); await _unitOfWork.SaveChangesAsync(); // Still no changes made to database transaction.Commit(); //Rollback will automatically be called by using in dispose method } } 

参考MSDN参考

博客有更清晰的描述

visualstudiomagazine.com 对于:当您调用SaveChanges时,在调用Transaction对象的Commit方法之前,您的所有更改都不会生效

编辑:

为了使事务范围与async-await一起工作,从.NET 4.5.1开始,您可以将TransactionScopeAsyncFlowOption.Enabled标志传递给其构造函数:

 using (var scope = new TransactionScope(... , TransactionScopeAsyncFlowOption.Enabled)) 

这可以确保事务范围与continuation表现良好。 有关更多信息,请参阅获取TransactionScope以使用async / await 。

请注意,从.NET 4.5.1开始,此function可用。

编辑2:

好的,在@Jcl评论BeingTransaction ,我搜索并找到了这个答案 :

随着EF6的推出, Microsoft建议使用新的API方法: Database.BeginTransaction()Database.UseTransaction() 。 System.Transactions.TransactionScope只是编写事务代码的旧样式。

Database.BeginTransaction() 仅用于与数据库相关的操作事务 ,而System.Transactions.TransactionScope使得可能的“普通C#代码”也是事务性的

TransactionScope的新异步function的限制:

  • 需要.NET 4.5.1或更高版本才能使用异步方法。

  • 除非您确定只有一个连接(云方案不支持分布式连接),否则它不能在云方案中使用
    交易)。

  • 它不能与前面部分的Database.UseTransaction()方法结合使用。

  • 如果您发出任何DDL,它将抛出exception(例如,因为
    数据库初始化程序)并且尚未启用分布式事务
    通过MSDTC服务。

考虑到限制,似乎启动EF6及更高版本的新方法是使用Database.BeginTransaction()而不是TransactionScope

总结:

这是编写异步事务作用域db调用的正确方法:

 public virtual async Task SaveAndCommitWithTransactionAsync(TEntity entity) { using (var transaction = _unitOfWork.BeginTransaction()) { try { Save(entity); await _unitOfWork.SaveChangesAsync(); transaction.Commit(); } catch (DbEntityValidationException e) { } } } 

请注意,如果您的作用域包含在using语句中,则不应调用transaction.RollBack() ,因为如果提交失败,则会进行回滚。

一个相关问题: Entity Framework 6事务回滚

这篇相关文章为新API提供了更多信息

边注:

这段代码:

 public virtual void SaveAndCommitAsync(TEntity entity) { try { Save(entity); _unitOfWork.SaveChangesAsync(); } catch (DbEntityValidationException e) { } } 

不做你认为它正在做的事情。 当您执行异步方法时, 通常应使用await关键字异步等待它。 这个方法:

  1. 使用void作为其返回类型。 如果这是一个异步API,则它至少需要async Taskasync void方法仅适用于事件处理程序 ,这里显然不是这种情况
  2. 最终用户可能正在等待这种方法,它应该变成:

     public virtual Task SaveAndCommitAsync(TEntity entity) { try { Save(entity); return _unitOfWork.SaveChangesAsync(); } catch (DbEntityValidationException e) { } } 

如果要包含事务范围 ,则必须等待此方法:

 public virtual async Task SaveAndCommitAsync(TEntity entity) { try { Save(entity); await _unitOfWork.SaveChangesAsync(); } catch (DbEntityValidationException e) { } } 

其余的异步方法也是如此。 一旦有交易,请确保等待该方法。

此外,不要吞下这样的例外,对它们做一些有用的事情,或者根本不抓住。