Domanda

Sto indagando sulle transazioni e sembra che si prendano cura di loro stessi in EF fino a quando passo false a SaveChanges () e quindi chiamo AcceptAllChanges () se non ci sono errori:

SaveChanges(false);
// ...
AcceptAllChanges();

Cosa succede se qualcosa va male? non devo eseguire il rollback o, non appena il mio metodo esce dal campo di applicazione, la transazione è terminata?

Cosa succede a tutte le colonne indipendenti che sono state assegnate a metà della transazione? Presumo che se qualcun altro ha aggiunto un record dopo il mio prima che il mio andasse male, ciò significa che ci sarà un valore di identità mancante.

C'è qualche motivo per usare la classe standard TransactionScope nel mio codice?

È stato utile?

Soluzione

Con Entity Framework il più delle volte SaveChanges () è sufficiente. Questo crea una transazione o si arruola in qualsiasi transazione ambientale e fa tutto il lavoro necessario in quella transazione.

Talvolta è utile l'accoppiamento SaveChanges (false) + AcceptAllChanges () .

Il posto più utile per questo è nelle situazioni in cui si desidera effettuare una transazione distribuita tra due diversi contesti.

vale a dire. qualcosa del genere (cattivo):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

Se context1.SaveChanges () ha esito positivo ma context2.SaveChanges () non riesce, l'intera transazione distribuita viene interrotta. Ma sfortunatamente Entity Framework ha già scartato le modifiche su context1 , quindi non è possibile riprodurre o registrare efficacemente l'errore.

Ma se cambi il tuo codice in questo modo:

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

Mentre la chiamata a SaveChanges (false) invia i comandi necessari al database, il contesto stesso non viene modificato, quindi è possibile farlo di nuovo se necessario, oppure è possibile interrogare il ObjectStateManager se lo desideri.

Ciò significa che se la transazione genera effettivamente un'eccezione, è possibile compensare, riprovando o registrando lo stato di ogni contesto ObjectStateManager da qualche parte.

Vedi my post sul blog per ulteriori informazioni

Altri suggerimenti

Se si utilizza EF6 (Entity Framework 6+), questo è cambiato per le chiamate del database a SQL.
Vedi: http://msdn.microsoft.com/en-us/data/dn456843.aspx

usa context.Database.BeginTransaction.

Da MSDN:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 

Perché alcuni database possono generare un'eccezione su dbContextTransaction.Commit (), quindi meglio:

using (var context = new BloggingContext()) 
{ 
  using (var dbContextTransaction = context.Database.BeginTransaction()) 
  { 
    try 
    { 
      context.Database.ExecuteSqlCommand( 
          @"UPDATE Blogs SET Rating = 5" + 
              " WHERE Name LIKE '%Entity Framework%'" 
          ); 

      var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
      foreach (var post in query) 
      { 
          post.Title += "[Cool Blog]"; 
      } 

      context.SaveChanges(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
} 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top