San*_*mar 10 .net c# entity-framework transactionscope async-await
我在stackoverflow上搜索但找不到类似的问题,请指出我是否有一个问题.
我试图用同步和异步操作实现一个通用的可重用存储库,但是我对实体框架和工作单元的了解很少,我很难找到实现它的正确方法.
我在SaveAndCommit操作中添加了一些变体,但不知道使用事务和异步执行此操作的最佳方法是什么.
- - 编辑 - -
根据我的理解,当执行多个操作时应该使用事务,但出于理解目的,我将它用于一个操作.(如果我错了,请纠正我)
这就是我到目前为止所做的
public class Service<TEntity> : IService<TEntity>
where TEntity : Entity
{
#region Constructor and Properties
UnitOfWork _unitOfWork { get { return UnitOfWork.UnitOfWorkPerHttpRequest; } }
protected DbSet<TEntity> Entities
{
get { return _unitOfWork.Set<TEntity>(); }
}
#endregion Constructor and Properties
#region Operations
public virtual IQueryable<TEntity> QueryableEntities()
{
return Entities;
}
public virtual async Task<IList<TEntity>> WhereAsync(Expression<Func<TEntity, bool>> predicate)
{
return await Entities.Where(predicate).ToListAsync();
}
public virtual IList<TEntity> Where(Expression<Func<TEntity, bool>> predicate)
{
return Entities.Where(predicate).ToList();
}
public virtual async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return await Entities.FirstOrDefaultAsync(predicate);
}
public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return Entities.FirstOrDefault(predicate);
}
public virtual async Task<TEntity> 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<int> CountAsync(Expression<Func<TEntity, bool>> predicate = null)
{
if (predicate != null)
{
return await Entities.CountAsync(predicate);
}
return await Entities.CountAsync();
}
#endregion Operations
}
Run Code Online (Sandbox Code Playgroud)
请指导我并建议实现这一目标的最佳方法.
现在看来,使用异步调用实现事务范围的正确方法是
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
}
}
Run Code Online (Sandbox Code Playgroud)
参考 MSDN参考
visualstudiomagazine.com 对于:当您调用SaveChanges时,在调用Transaction对象的Commit方法之前,您的所有更改都不会生效
Yuv*_*kov 22
编辑:
为了使事务范围与async-await.NET 4.5.1 一起工作,您可以将TransactionScopeAsyncFlowOption.Enabled标志传递给它的构造函数:
using (var scope = new TransactionScope(... ,
TransactionScopeAsyncFlowOption.Enabled))
Run Code Online (Sandbox Code Playgroud)
这可以确保事务范围与continuation的行为很好.有关更多信息,请参阅获取TransactionScope以使用async/await.
请注意,从.NET 4.5.1开始,此功能可用.
编辑2:
好的,在@Jcl评论之后BeingTransaction,我搜索并找到了这个答案:
随着EF6的推出,Microsoft建议使用新的API 方法:
Database.BeginTransaction()和Database.UseTransaction().System.Transactions.TransactionScope只是编写事务代码的旧样式.但
Database.BeginTransaction()使用仅适用于数据库相关的业务交易,而System.Transactions.TransactionScope使得可能的"纯C#代码"也交易.
新异步功能的局限性TransactionScope:
需要.NET 4.5.1或更高版本才能使用异步方法.
除非您确定只有一个连接(云方案不支持分布式
事务),否则它不能在云方案中使用.
它不能与Database.UseTransaction()前面部分的方法结合使用.
如果您发出任何DDL(例如,由于
数据库初始化程序)并且未
通过MSDTC服务启用分布式事务,它将抛出异常.
考虑到限制,似乎使用Database.BeginTransaction()TransactionScope EF6及以上版本的新方法代替.
这是编写异步事务作用域db调用的正确方法:
public virtual async Task SaveAndCommitWithTransactionAsync(TEntity entity)
{
using (var transaction = _unitOfWork.BeginTransaction())
{
try
{
Save(entity);
await _unitOfWork.SaveChangesAsync();
transaction.Commit();
}
catch (DbEntityValidationException e)
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,transaction.RollBack()如果您的作用域包含在using语句中,则不应调用,因为如果提交失败,则会进行回滚.
相关问题:实体框架6事务回滚
这篇相关文章为新API提供了更多信息
这段代码:
public virtual void SaveAndCommitAsync(TEntity entity)
{
try
{
Save(entity);
_unitOfWork.SaveChangesAsync();
}
catch (DbEntityValidationException e)
{
}
}
Run Code Online (Sandbox Code Playgroud)
不做你认为它正在做的事情.当您执行异步方法时,通常应该使用await关键字异步等待它.这个方法:
void作为它的返回类型.如果这是异步API,则至少 需要它async Task.async void方法只适用于事件处理程序,这里显然不是这种情况最终用户可能正在等待这种方法,它应该变成:
public virtual Task SaveAndCommitAsync(TEntity entity)
{
try
{
Save(entity);
return _unitOfWork.SaveChangesAsync();
}
catch (DbEntityValidationException e)
{
}
}
Run Code Online (Sandbox Code Playgroud)如果要包含事务范围,则必须等待此方法:
public virtual async Task SaveAndCommitAsync(TEntity entity)
{
try
{
Save(entity);
await _unitOfWork.SaveChangesAsync();
}
catch (DbEntityValidationException e)
{
}
}
Run Code Online (Sandbox Code Playgroud)
其余的异步方法也是如此.一旦有交易,请确保等待该方法.
此外,不要吞下这样的例外,对它们做一些有用的事情,或者根本不抓住.
| 归档时间: |
|
| 查看次数: |
10031 次 |
| 最近记录: |