Utilizar transacções ou alterações(false) e aceitar todas as alterações()?

tenho investigado transacções e parece que eles se cuidam em EF desde que eu passe false para SaveChanges() e depois ligue AcceptAllChanges() Se não houver erros:

SaveChanges(false);
// ...
AcceptAllChanges();
E se alguma coisa correr mal? Não tenho de voltar atrás ou, assim que o meu método sair do alcance, a transacção acaba?

O que acontece a qualquer coluna indentiy que tenha sido atribuída a meio da transacção? Presumo que se alguém adicionasse um disco atrás do meu antes do meu ir mau então isso significa que haverá um valor de identidade faltando.

Há alguma razão para usar a classe standard TransactionScope no meu código?

Author: Liam, 2009-05-03

3 answers

Com a estrutura da entidade na maior parte do tempo SaveChanges() é suficiente. Isso cria uma transação, ou se alista em qualquer transação ambiente, e faz todo o trabalho necessário nessa transação.

Por vezes, embora o emparelhamento seja útil.

O lugar mais útil para isto é em situações onde você quer fazer uma transação distribuída em dois contextos diferentes.

Ou seja, algo assim.
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() for bem sucedido mas context2.SaveChanges() falhar toda a transacção distribuída é abortada. Mas infelizmente a estrutura da entidade já descartou as alterações em context1, então você não pode repetir ou registrar efetivamente a falha.

Mas se mudares o teu código para seres assim:

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

}

Enquanto a chamada para SaveChanges(false) envia os comandos necessários para a base de dados, o contexto em si não é alterado, por isso pode fazê-lo de novo se necessário, ou pode interrogar o ObjectStateManager se quiser.

Isto significa que se a transacção na verdade, lança uma excepção que você pode compensar, quer tentando de novo ou registrando o estado de cada contexto ObjectStateManager em algum lugar.

Ver meu blog post para mais.

 431
Author: Alex James, 2016-10-20 06:03:54

Se estiver a utilizar EF6 (Entity Framework 6+), isto foi alterado para chamadas de bases de dados para SQL.
Ver: http://msdn.microsoft.com/en-us/data/dn456843.aspx

Usar o contexto.Banco.Iniciar transmissão.

De 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
        } 
    } 
} 
 101
Author: user3885816, 2016-04-03 12:40:23

Porque alguma base de dados pode abrir uma excepção na tradução do DbContext.Commit () so better this:

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(); 
    } 
  } 
} 
 -4
Author: eMeL, 2015-03-06 02:19:31