【问题标题】:Using TransactionScope with Entity Framework 6将 TransactionScope 与实体框架 6 一起使用
【发布时间】:2016-03-09 02:49:27
【问题描述】:

我无法理解的是是否可以对上下文进行更改并在提交之前在同一个事务中获取更改。

这就是我要找的:

using (var scope = new TransactionScope(TransactionScopeOption.Required)) 
{ 
    using (var context = new DbContext()) 
    { 
        //first I want to update an item in the context, not to the db
        Item thisItem = context.Items.First();
        thisItem.Name = "Update name";
        context.SaveChanges(); //Save change to this context

        //then I want to do a query on the updated item on the current context, not against the db
        Item thisUpdatedItem = context.Items.Where(a=>a.Name == "Update name").First();

        //do some more query
    } 

    //First here I want it to commit all the changes in the current context to the db
    scope.Complete(); 
} 

有人可以帮我理解并展示一个工作模式吗?

【问题讨论】:

    标签: c# entity-framework-6 dbcontext transactionscope savechanges


    【解决方案1】:

    是的,当您想要将实体插入数据库并使用自动生成的 id 进行下一次插入或更新时,这是可以做到的,并且非常有用

    using (var context = new DbContext())     
    { 
        using (var transaction = context.Database.BeginTransaction()) {
            var item = new Item();
            context.Items.Insert(item);
            context.SaveChanges(); // temporary insert to db to get back the auto-generated id
    
            // do some other things
            var otherItem = context.OtherItems.First();
            // use the inserted id
            otherItem.Message = $"You just insert item with id = {item.Id} to database";
            transaction.Commit();
        }
    } 
    

    因为您的问题还询问了工作模式,所以这是我的工作代码(使用 FluentApi、DbContext 和事务)。我和你有同样的问题:)。希望对你有帮助

    public class FluentUnitOfWork : IDisposable
    {
        private DbContext Context { get; }
    
        private DbContextTransaction Transaction { get; set; }
    
        public FluentUnitOfWork(DbContext context)
        {
            Context = context;
        }
    
        public FluentUnitOfWork BeginTransaction()
        {
            Transaction = Context.Database.BeginTransaction();
            return this;
        }
    
        public FluentUnitOfWork DoInsert<TEntity>(TEntity entity) where TEntity : class
        {
            Context.Set<TEntity>().Add(entity);
            return this;
        }
    
        public FluentUnitOfWork DoInsert<TEntity>(TEntity entity, out TEntity inserted) where TEntity : class
        {
            inserted = Context.Set<TEntity>().Add(entity);
            return this;
        }
    
        public FluentUnitOfWork DoUpdate<TEntity>(TEntity entity) where TEntity : class
        {
            Context.Entry(entity).State = EntityState.Modified;
            return this;
        }
    
        public FluentUnitOfWork SaveAndContinue()
        {
            try
            {
                Context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                // add your exception handling code here
            }
            return this;
        }
    
        public bool EndTransaction()
        {
            try
            {
                Context.SaveChanges();
                Transaction.Commit();
            }
            catch (DbEntityValidationException dbEx)
            {
                // add your exception handling code here
            }
            return true;
        }
    
        public void RollBack()
        {
            Transaction.Rollback();
            Dispose();
        }
    
        public void Dispose()
        {
            Transaction?.Dispose();
            Context?.Dispose();
        }
    }
    

    示例用法:

    var status = BeginTransaction()
                    // First Part
                    .DoInsert(entity1)
                    .DoInsert(entity2)
                    .DoInsert(entity3)
                    .DoInsert(entity4)
                    .SaveAndContinue()
                    // Second Part
                    .DoInsert(statusMessage.SetPropertyValue(message => message.Message, $"Just got new message {entity1.Name}"))
                .EndTransaction();
    

    【讨论】:

    • 这会在数据库中插入行,然后如果事务被回滚,它会删除该行吗?或者你如何获得行的ID? @kienct89
    • 在调用transaction.Commit()之前不会在db中插入行,id会临时分配到内存中
    • 好的,谢谢你的例子和描述。我会对此进行测试并回复您
    • TransactionScope 回答 Database.BeginTransaction 的问题。他是如何真正接受的? TransactionScope 有答案吗?
    • 好主意!想到 DoBulkInsert 也会很棒sensibledev.com/entity-framework-bulk-insert
    【解决方案2】:

    如果您想确保只查询上下文的本地内容,您可以使用“本地”集合:

    Item thisItem = context.Items.First();  
    thisItem.Name = "Update name";    
    Item thisUpdatedItem = context.Items.Local.Where(a=>a.Name == "Update name").First();
    

    这只会查询上下文的内存数据,不会命中数据库。
    只要您通过添加对象或从数据库加载对象在上下文中具体化对象,“本地”数据就会出现,即您无需调用 SaveChanges()。
    SaveChanges() 会将上下文的内容写入您的数据库。

    【讨论】:

    • 这看起来很棒。我有一个问题;正如 kienct89 在他的回答中描述的那样,它处理插入并在数据库中自动生成 ID。 “本地”有没有类似的功能?
    • 对于新记录,“本地”只是一个暂存区。 ID 由数据库在插入记录时提供。它是一个数据库函数,而不是一个 EF 函数。
    【解决方案3】:

    根据我的经验,创建上下文不是必需的,我喜欢尽可能简化,因此如果您需要在回滚时触发代码,请在事务周围加上 try catch。

    try
    {
       using (var scope = new TransactionScope(TransactionScopeOption.Required))
       {
              ...do stuff
    
              scope.Complete();
       }
    }
    catch (Exception)
    {
      ...do stuff
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-23
      相关资源
      最近更新 更多