Utilisation de transactions ou SaveChanges (false) et AcceptAllChanges ()?

c# entity-framework transactions

Question

J'ai enquêté sur des transactions et il semble qu'elles se prennent en charge dans EF aussi longtemps que je passe false à SaveChanges() , puis que j'appelle AcceptAllChanges() s'il n'y a pas d'erreur:

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

Et si quelque chose va mal? Ne dois-je pas revenir en arrière ou, dès que ma méthode sort du cadre, la transaction est-elle terminée?

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

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

Réponse acceptée

SaveChanges() suffit généralement avec Entity Framework. Cela crée une transaction ou s’inscrit dans une transaction ambiante et effectue tout le travail nécessaire dans cette transaction.

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

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

C'est à dire quelque chose comme ça (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. Mais malheureusement, Entity Framework a déjà ignoré les modifications apportées à context1 , vous ne pouvez donc pas relire ou enregistrer efficacement l'échec.

Mais si vous changez votre code pour ressembler à 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 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 post de blog pour plus.


Réponse populaire

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.

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


Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow