14

Nie mogę zrozumieć, czy możliwe jest wprowadzenie zmian w kontekście i uzyskanie zmian w tej samej transakcji przed jej zatwierdzeniem.Używanie TransactionScope z Entity Framework 6

To co jestem szukasz:

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

Czy ktoś może mi pomóc zrozumieć i pokazać mi wzór działa?

Odpowiedz

29

Tak, jest to możliwe do zrobienia i to bardzo przydatne, gdy chcesz wstawić podmiot do bazy i używać automatycznie wygenerowane identyfikator dla następnej wkładki lub zaktualizować

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

Ze względu na swoje pytanie zapytano także o działający wzorzec, oto mój działający kod (przy użyciu FluentApi, DbContext & Transakcja). Miałem ten sam problem co ty :). Nadzieję, że to pomaga

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

użycie Sample:

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

Czy to wstawi wiersz do bazy danych, a następnie, jeśli transakcja zostanie wycofana, czy usunie wiersz? Lub jak otrzymasz identyfikator rzędu? @ kienct89 –

+0

to nie wstawi wiersza w db, dopóki nie wywołasz transaction.care(), identyfikator zostanie tymczasowo przypisany do pamięci –

+0

ok, dziękuję za przykład i opis. Testuję to i wracam do ciebie –

2

Jeśli chcesz się upewnić, że tylko zapytania lokalną zawartość swojej kontekście można użyć „lokalny” kolekcję:

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

Ten wyśle ​​zapytanie tylko dane w pamięci kontekstu i będzie nie trafił w bazę danych.
Dane "lokalne" są dostępne, gdy tylko zmaterializujesz obiekt w kontekście, dodając go lub pobierając z bazy danych, tzn. Nie musisz wywoływać funkcji SaveChanges().
SaveChanges() zapisze zawartość kontekstu w bazie danych.

+0

Wydaje niesamowite. Mam jedno pytanie; jak kienct89 opisane w jego odpowiedzi obsługuje insert z automatycznym generowaniem ID w db. Czy "lokalny" ma podobną funkcję? –

+0

Nowe rekordy "Lokalne" to tylko obszar pomostowy. Identyfikator jest dostarczany przez bazę danych po włożeniu rekordu. Jest to funkcja bazy danych, a nie funkcja EF. –

Powiązane problemy