使用TransactionScope与Entity Framework 6。

时间:2021-04-07 02:30:40

What I can't understand is if its possible to make changes to the context and get the changes in the same transaction before its commited.

我无法理解的是,如果它可能对上下文进行更改,并在它开始之前得到相同事务中的更改。

This is what I´m looking for:

这就是我´m寻找:

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(); 
} 

Can someone help me understand and show me a working pattern?

有人能帮我了解一下工作模式吗?

2 个解决方案

#1


31  

Yes, it's possible to do and it's very useful when you want to insert a entity to database and use the auto-generated id for the next insert or update

是的,这是可能的,当你想要插入一个实体到数据库,并使用自动生成的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();
    }
} 

Because your question also asked about a working pattern, here's my working code (with use of FluentApi, DbContext & Transaction). I was having the same issue as you :). Hope it helps you

因为您的问题还询问了工作模式,下面是我的工作代码(使用FluentApi、DbContext & Transaction)。我和你有同样的问题。希望它能帮助你

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();
    }
}

Sample usage:

示例用法:

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();

#2


2  

If you want to make sure that you only query the local content of your context you can use the "local" collection:

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

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

This will only query the in-memory data of the context and will not hit the database.
The "Local" data is present as soon as you materialize an object in the context by adding it or loading it from the database, i.e. you do not need to call SaveChanges().
SaveChanges() will write the content of the context to your database.

这只会查询上下文的内存数据,不会访问数据库。当您在上下文中通过添加或从数据库加载对象时,“本地”数据就出现了,即您不需要调用SaveChanges()。SaveChanges()将把上下文的内容写到数据库中。

#1


31  

Yes, it's possible to do and it's very useful when you want to insert a entity to database and use the auto-generated id for the next insert or update

是的,这是可能的,当你想要插入一个实体到数据库,并使用自动生成的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();
    }
} 

Because your question also asked about a working pattern, here's my working code (with use of FluentApi, DbContext & Transaction). I was having the same issue as you :). Hope it helps you

因为您的问题还询问了工作模式,下面是我的工作代码(使用FluentApi、DbContext & Transaction)。我和你有同样的问题。希望它能帮助你

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();
    }
}

Sample usage:

示例用法:

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();

#2


2  

If you want to make sure that you only query the local content of your context you can use the "local" collection:

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

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

This will only query the in-memory data of the context and will not hit the database.
The "Local" data is present as soon as you materialize an object in the context by adding it or loading it from the database, i.e. you do not need to call SaveChanges().
SaveChanges() will write the content of the context to your database.

这只会查询上下文的内存数据,不会访问数据库。当您在上下文中通过添加或从数据库加载对象时,“本地”数据就出现了,即您不需要调用SaveChanges()。SaveChanges()将把上下文的内容写到数据库中。