Question

J'ai étudié des transactions et il semble qu'elles se prennent en charge dans EF tant que je transmets false à SaveChanges () , puis que j'appelle AcceptAllChanges. () s'il n'y a pas d'erreur:

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

Et si quelque chose se passait mal? ne dois-je pas revenir en arrière ou, dès que ma méthode est hors de portée, la transaction est-elle terminée?

Qu'advient-il des colonnes d'identification attribuées au milieu de la transaction? Je présume que si quelqu'un d'autre ajoute un disque après le mien avant que le mien ne se détériore, cela signifie qu'il manquera une valeur d'identité.

Existe-t-il une raison d'utiliser la classe standard TransactionScope dans mon code?

Était-ce utile?

La solution

Avec Entity Framework, la plupart du temps SaveChanges () est suffisant. Cela crée une transaction ou s’inscrit dans une transaction ambiante et effectue tout le travail nécessaire dans cette transaction.

Parfois, le couplage SaveChanges (false) + AcceptAllChanges () est utile.

L'endroit le plus utile pour cela est dans les situations où vous souhaitez effectuer une transaction distribuée sur deux contextes différents.

I.e. quelque chose comme ceci (mauvais):

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 () réussit mais que context2.SaveChanges () échoue, toute la transaction distribuée est abandonnée. Malheureusement, Entity Framework a déjà ignoré les modifications apportées à context1 . Par conséquent, vous ne pouvez pas relire ou enregistrer correctement l'échec.

Mais si vous modifiez votre code pour qu'il ressemble à ceci:

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

}

Alors que l'appel à SaveChanges (false) envoie les commandes nécessaires à la base de données, le contexte lui-même n'est pas modifié. Vous pouvez donc le répéter si nécessaire ou interroger le . ObjectStateManager si vous le souhaitez.

Cela signifie que si la transaction lève une exception, vous pouvez compenser, soit en essayant à nouveau, soit en enregistrant l'état de chaque contexte ObjectStateManager quelque part.

Voir mon billet de blog pour plus d'informations.

Autres conseils

Si vous utilisez EF6 (Entity Framework 6+), cela a changé pour les appels de base de données à SQL.
Voir: http://msdn.microsoft.com/en-us/data/dn456843.aspx

utilisez context.Database.BeginTransaction.

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

Parce que certaines bases de données peuvent générer une exception à dbContextTransaction.Commit (), il est donc préférable que ceci:

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(); 
    } 
  } 
} 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top