首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架6异步操作和TranscationScope

实体框架6异步操作和TranscationScope
EN

Stack Overflow用户
提问于 2015-01-25 06:13:01
回答 1查看 15.6K关注 0票数 15

我搜索堆叠溢出,但找不到类似的问题,请指点我,如果已经有一个。

我试图用同步操作和异步操作实现一个通用的可重用存储库,但由于我对实体框架和工作单元知之甚少,我很难找到实现它的正确方法。

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

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

这就是我迄今为止所做的

代码语言:javascript
复制
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

}

请指导我,并提出实现这一目标的最佳方法。

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

代码语言:javascript
复制
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 For :当您调用SaveChanges时,在调用事务对象的提交方法之前,所有更改都不会生效

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-01-25 06:31:01

编辑:

为了使事务作用域与async-await协同工作,从.NET 4.5.1开始,您可以将TransactionScopeAsyncFlowOption.Enabled标志传递给它的构造函数:

代码语言:javascript
复制
using (var scope = new TransactionScope(... ,
  TransactionScopeAsyncFlowOption.Enabled))

这可以确保事务作用域在连续过程中表现良好。有关更多信息,请参见让TransactionScope使用异步/等待

注意,自从.NET 4.5.1之后,这个特性是可用的。

编辑2:

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

随着EF6的引入,微软建议使用新的API方法:Database.BeginTransaction()Database.UseTransaction()。System.Transactions.TransactionScope只是编写事务性代码的旧风格。 但是Database.BeginTransaction()只用于与数据库相关的操作事务,而System.Transactions.TransactionScope使可能的‘普通C#代码’也是事务性

TransactionScope新异步特性的局限性

  • 需要.NET 4.5.1或更高版本才能使用异步方法。
  • 它不能在云场景中使用,除非您确定只有一个连接(云方案不支持分布式连接)。 (交易)。
  • 它不能与前面各节的Database.UseTransaction()方法相结合。
  • 如果您发出任何DDL (例如,由于 数据库初始化程序),并且没有启用分布式事务。 通过MSDTC服务。

考虑到这些限制,启动EF6和更高版本的新方法似乎是使用而不是 TransactionScope,。

最后:

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

代码语言:javascript
复制
public virtual async Task SaveAndCommitWithTransactionAsync(TEntity entity)
{
    using (var transaction = _unitOfWork.BeginTransaction())
    {
        try
        {
            Save(entity);
            await _unitOfWork.SaveChangesAsync();

            transaction.Commit();
        }
        catch (DbEntityValidationException e)
        {
        }
    }
}

请注意,如果您的作用域被包装在一个transaction.RollBack()语句中,则不应该调用using,因为如果提交失败,它将接受回滚。

一个相关问题:实体框架6事务回滚

这篇相关文章提供了更多关于新API的信息。

附带说明:

这段代码:

代码语言:javascript
复制
public virtual void SaveAndCommitAsync(TEntity entity)
{
    try
    {
        Save(entity);
        _unitOfWork.SaveChangesAsync();
    }
    catch (DbEntityValidationException e)
    {
    }
}

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

  1. 使用void作为其返回类型。如果这是一个异步API,它至少需要是async Taskasync void方法只适用于事件处理程序,这里显然不是这样的
  2. 最终用户可能正在等待此方法,应将其转换为: 公共虚拟任务SaveAndCommitAsync(TEntity实体){尝试{保存(实体);返回_unitOfWork.SaveChangesAsync();} catch (DbEntityValidationException e) {}

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

代码语言:javascript
复制
public virtual async Task SaveAndCommitAsync(TEntity entity)
{
    try
    {
        Save(entity);
        await _unitOfWork.SaveChangesAsync();
    }
    catch (DbEntityValidationException e)
    {
    }
}

其他异步方法也是如此。一旦有了事务,请确保等待该方法。

此外,不要吞下这样的异常,做一些有用的事情,或者干脆不抓住。

票数 28
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28133801

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档