Pregunta

He estado investigando transacciones y parece que se cuidan en EF siempre que pase false a SaveChanges () y luego llame a AcceptAllChanges () si no hay errores:

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

¿Qué pasa si algo va mal? ¿no tengo que revertir o, tan pronto como mi método queda fuera del alcance, se termina la transacción?

¿Qué sucede con las columnas independientes que se asignaron a mitad de la transacción? Supongo que si alguien más agregó un registro después del mío antes de que el mío se echara a perder, significa que faltará un valor de Identidad.

¿Hay alguna razón para usar la clase TransactionScope estándar en mi código?

¿Fue útil?

Solución

Con Entity Framework, la mayoría de las veces, SaveChanges () es suficiente. Esto crea una transacción, o se enlista en cualquier transacción ambiental, y hace todo el trabajo necesario en esa transacción.

A veces, aunque el emparejamiento SaveChanges (false) + AcceptAllChanges () es útil.

El lugar más útil para esto es en situaciones en las que desee realizar una transacción distribuida en dos Contextos diferentes.

I.e. algo como esto (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();
}

Si context1.SaveChanges () tiene éxito pero context2.SaveChanges () falla, se cancela toda la transacción distribuida. Pero, desafortunadamente, el Entity Framework ya ha descartado los cambios en context1 , por lo que no puede volver a reproducir o registrar efectivamente el error.

Pero si cambias tu código para que se vea así:

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

}

Mientras la llamada a SaveChanges (false) envía los comandos necesarios a la base de datos, el contexto en sí no cambia, por lo que puede hacerlo nuevamente si es necesario, o puede interrogar al ObjectStateManager si lo desea.

Esto significa que si la transacción realmente genera una excepción que puede compensar, volviendo a intentar o registrando el estado de cada contexto ObjectStateManager en alguna parte.

Consulte my publicación de blog para obtener más información.

Otros consejos

Si está utilizando EF6 (Entity Framework 6+), esto ha cambiado para las llamadas de base de datos a SQL.
Consulte: http://msdn.microsoft.com/en-us/data/dn456843.aspx

use context.Database.BeginTransaction.

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

Debido a que algunas bases de datos pueden generar una excepción en dbContextTransaction.Commit (), mejor esto:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top