实体框架6异步操作和TranscationScope [英] Entity Framework 6 async operations and TranscationScope

查看:208
本文介绍了实体框架6异步操作和TranscationScope的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I search on stackoverflow but could not find a similar question, please point me if there is already one.

我正在尝试实现一个通用的可重用存储库同步和异步操作,但是我对实体框架和工作单位的了解很少我正在努力寻找实现它的正确方法。

I was trying to implement a generic reusable repository with both sync and async operations but with my little knowledge with Entity Framework and Unit Of Work I'm struggling to find the correct way to implement it.

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

I have added some variations on SaveAndCommit operation but don't know what is the best way to do it with transaction and async.


----编辑----

----Edit----

根据我的理解,当多个操作时应该使用事务新新200的X- 200 X- 200新新新200新新新200新新新200新新新200新新200新新新200新新200新新新200新新200新新200新新新200新新200新新200新新(如果我错了,请更正我)

As per my understanding transactions should be used when more than one operations is performed but for understanding purposes I used it for one operation. (Please correct me if I'm wrong)

这是我迄今为止做的

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

}

请指导并建议最佳方法来实现。

Please guide me and suggest the best way to achieve this.


现在似乎使用异步调用实现事务范围的正确方法将是

Now it seems that the correct way to implement a transaction scope with async call would be



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
        }
    }

$ b将自动调用回滚
$ b

参考
MSDN参考

References MSDN Reference

具有更清晰描述的博客

visualstudiomagazine.com 调用SaveChanges时,没有任何更改将在调用Transaction对象的Commit方法之前生效。

visualstudiomagazine.com For : when you call SaveChanges, none of your changes will take effect until you call the Transaction object's Commit method

推荐答案

编辑

为了让交易范围与 async-await ,从.NET 4.5.1开始,您可以将 TransactionScopeAsyncFlowOption.Enabled 标志传递给其构造函数:

In order for transaction scopes to work together with async-await, starting from .NET 4.5.1 you can pass in a TransactionScopeAsyncFlowOption.Enabled flag to its constructor:

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

事务范围与连续性表现良好。
请参见将TransactionScope与异步/等待配合使用

This makes sure that the transaction scopes behaves nicely with continuations. See Get TransactionScope to work with async / await for more.

请注意,此功能自.NET 4.5.1起可用。

Note this feature is available since .NET 4.5.1 onward.

编辑2 :

好吧,在@JCL评论 BeingTransaction 之后,我搜索并发现这个答案

Okay, after @Jcl comment on BeingTransaction, i searched and found this answer:


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

With the introduction of EF6, Microsoft recommends to use new API methods: Database.BeginTransaction() and Database.UseTransaction(). System.Transactions.TransactionScope is just old style of writing transactional code.

但是$ code Database.BeginTransaction()仅用于与数据库相关的
操作事务,而 System.Transactions.TransactionScope

But Database.BeginTransaction() is used only for database related operations transaction, whereas System.Transactions.TransactionScope makes the possible 'plain C# code' also transactional.

<$ c $的新异步功能的局限性c> TransactionScope :


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

  • Requires .NET 4.5.1 or greater to work with asynchronous methods.

它不能在云方案中使用,除非你确定你有一个
和只有一个连接(云方案不支持分布式

事务)。

It cannot be used in cloud scenarios unless you are sure you have one and only one connection (cloud scenarios do not support distributed
transactions).

不能与 Database.UseTransaction()
以前的部分。

It cannot be combined with the Database.UseTransaction() approach of the previous sections.

如果您发出任何DDL,它将抛出异常(例如因为的一个

数据库初始化程序),并且尚未通过MSDTC服务启用分布式事务

It will throw exceptions if you issue any DDL (e.g. because of a
Database Initializer) and have not enabled distributed transactions
through the MSDTC Service.

似乎启动EF6及以上版本的新方法是使用 Database.BeginTransaction()而不是 TransactionScope ,给予限制。

It seems like the new approach starting EF6 and above is to use Database.BeginTransaction() instead of TransactionScope, given the limitations.

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

This is the proper way to write async transaction scoped db calls:

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() >使用语句,因为如果提交不成功将需要回滚。

Note that transaction.RollBack() should not be called in case your scope is wrapped in a using statement, as it will take of the rollback if the commit was unsuccessful.

相关问题:实体框架6交易回滚

此相关文章更多地介绍了新的API

This related article sheds more light on the new API

这段代码:

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

没有做你认为这样做的事情。当您执行异步方法时,您应该使用 await 关键字异步地等待。这个方法:

Isn't doing what you think it's doing. When you execute a method which is asynchronous, you should usually asynchronously wait on it using the await keyword. This method:


  1. 使用 void 作为其返回类型。如果这是异步API,则需要至少 async Task async void 方法仅适用于事件处理程序,这里显然不是这种情况

  2. 最终用户可能正在等待这种方法,它应该变成:

  1. Is using void as its return type. If this is an asynchronous API, it needs to be at least async Task. async void methods are only ment for event handlers, where this clearly isn't the case here
  2. The end user will probably be awaiting on this method, it should be turned into:

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


如果要包含一个事务范围,那么这个方法必须等待:

If you want to include a Transaction Scope, then this method must be awaited:

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

与其他异步方法相同。一旦一个交易在那里,请确保你等待着这个方法。

Same goes for the rest of your asynchronous methods. Once a transaction is there, make sure you await on the method.

另外,不要像这样做一些异常的事情,或者只是不要抓住。

Also, don't swallow exceptions like that, do something useful with them, or simply don't catch.

这篇关于实体框架6异步操作和TranscationScope的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆