Search code examples
c#entity-framework-6dbcontexttransactionscopesavechanges

Using TransactionScope with Entity Framework 6


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:

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?


Solution

  • 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

    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

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