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