Pergunta

Eu têm vindo a investigar transações e parece que eles cuidar de si na EF, enquanto eu passar false para SaveChanges() e depois chamar AcceptAllChanges() se não há erros:

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

E se algo vai mal? Não tenho a reversão ou, tão logo o meu método sai do escopo, é a operação terminou?

O que acontece com as colunas indentiy que foram atribuídos a meio da transação? Eu presumo que se alguém adicionou um registro após o meu antes meu foi ruim, então isso significa que haverá um valor de identidade faltando.

Existe alguma razão para usar a classe TransactionScope padrão no meu código?

Foi útil?

Solução

Com o Entity Framework a maior parte do SaveChanges() tempo é suficiente. Isso cria uma transação, ou pede em qualquer transação de ambiente, e faz todo o trabalho necessário em que transação.

Às vezes, porém o emparelhamento SaveChanges(false) + AcceptAllChanges() é útil.

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

i. algo assim (mal):

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() tiver êxito, mas context2.SaveChanges() falhar toda a transação distribuída é abortada. Mas, infelizmente, o Entity Framework já descartadas as alterações no context1, para que você não pode repetir ou efetivamente registrar a falha.

Mas se você alterar seu código para olhar como esta:

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 o banco de dados, o próprio contexto não é alterado, para que possa fazê-lo novamente, se necessário, ou você pode interrogar o ObjectStateManager se quiser.

Isto significa que se a transação realmente gera uma exceção você pode compensar, por qualquer re-tentar ou registro de estado de cada contextos ObjectStateManager em algum lugar.

Consulte minha blog para mais.

Outras dicas

Se você estiver usando EF6 (Entity Framework 6+), isso mudou para chamadas de banco de dados para SQL.
Veja: http://msdn.microsoft.com/en-us/data/dn456843.aspx

uso context.Database.BeginTransaction.

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
        } 
    } 
} 

Porque algum banco de dados pode lançar uma exceção em dbContextTransaction.Commit () então é melhor isto:

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(); 
    } 
  } 
} 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top