web-dev-qa-db-fra.com

Annulation de transaction Entity Framework 6

Avec EF6, vous avez une nouvelle transaction qui peut être utilisée comme:

using (var context = new PostEntityContainer())
        {
            using (var dbcxtransaction = context.Database.BeginTransaction())
            {
                try
                {
                    PostInformation NewPost = new PostInformation()
                    {
                        PostId = 101,
                        Content = "This is my first Post related to Entity Model",
                        Title = "Transaction in EF 6 beta"
                    };
                    context.Post_Details.Add(NewPost);
                    context.SaveChanges();
                    PostAdditionalInformation PostInformation = new PostAdditionalInformation()
                    {
                        PostId = (101),
                        PostName = "Working With Transaction in Entity Model 6 Beta Version"
                    };

                    context.PostAddtional_Details.Add(PostInformation);
                    context.SaveChanges();

                    dbcxtransaction.Commit();
                }
                catch
                {
                    dbcxtransaction.Rollback();
                }
            }
        }

Le retour en arrière est-il réellement nécessaire lorsque les choses vont de travers? Je suis curieux car la description de validation indique: "Valide la transaction de magasin sous-jacente".

Tandis que la description de l'annulation dit: "Annule la transaction de magasin sous-jacente".

Cela me rend curieux, car il me semble que si Commit n'est pas appelé, les commandes précédemment exécutées ne seront pas stockées (ce qui me semble logique). Mais si tel est le cas, quelle serait la raison pour appeler la fonction Rollback? En EF5, j’utilisais TransactionScope, qui ne comportait pas de fonction de retour en arrière (seulement une complète), ce qui me paraissait logique. Pour des raisons liées à MS DTC, je ne peux plus utiliser TransactionScope, mais je ne peux pas non plus utiliser un catch try comme dans l'exemple ci-dessus (c'est-à-dire que je n'ai besoin que du Commit).

72
The Cookies Dog

Vous n'avez pas besoin d'appeler Rollback manuellement car vous utilisez l'instruction using.

La méthode DbContextTransaction.Dispose Sera appelée à la fin du bloc using. Et il annulera automatiquement la transaction si la transaction n'est pas validée avec succès (exceptions non appelées ou rencontrées). Voici le code source de la méthode SqlInternalTransaction.Dispose (DbContextTransaction.Dispose Lui sera finalement délégué lors de l'utilisation du fournisseur SqlServer):

private void Dispose(bool disposing)
{
    // ...
    if (disposing && this._innerConnection != null)
    {
        this._disposing = true;
        this.Rollback();
    }
}

Vous voyez, il vérifie si _innerConnection N'est pas nul, sinon, annulez la transaction (si elle est validée, _innerConnection Sera nulle). Voyons ce que Commit fait:

internal void Commit() 
{
    // Ignore many details here...

    this._innerConnection.ExecuteTransaction(...);

    if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer)
    {
        // Zombie() method will set _innerConnection to null
        this.Zombie();
    }
    else
    {
        this.ZombieParent();
    }

    // Ignore many details here...
}

internal void Zombie()
{
    this.ZombieParent();

    SqlInternalConnection innerConnection = this._innerConnection;

    // Set the _innerConnection to null
    this._innerConnection = null;

    if (innerConnection != null)
    {
        innerConnection.DisconnectTransaction(this);
    }
}
105
Mouhong Lin

Tant que vous utiliserez toujours SQL Server avec EF, il n'est pas nécessaire d'utiliser explicitement l'attrape pour appeler la méthode Rollback. Permettre au bloc using de revenir automatiquement sur les exceptions fonctionnera toujours.

Toutefois, lorsque vous y réfléchissez du point de vue de Entity Framework, vous pouvez comprendre pourquoi tous les exemples utilisent l'appel explicite à Annulation de la transaction. Pour le EF, le fournisseur de base de données est arbitraire et connectable et le fournisseur peut être remplacé par MySQL ou toute autre base de données ayant une implémentation de fournisseur EF. Par conséquent, du point de vue de l'EF, rien ne garantit que le fournisseur annulera automatiquement la transaction supprimée, car l'EF ne connaît pas l'implémentation du fournisseur de base de données.

Par conséquent, il est recommandé de vous reporter explicitement à la restauration dans le cadre de la documentation EF, au cas où vous changeriez un jour de fournisseur en implémentation ne prenant pas automatiquement en compte la suppression.

À mon avis, tout fournisseur compétent et bien rédigé annulera automatiquement la transaction lors de la cession, de sorte que l'effort supplémentaire pour envelopper tout dans le bloc using avec un try-catch-rollback est excessif.

21
Rwb
  1. Étant donné que vous avez écrit un bloc 'using' pour instancier la transaction, vous n'avez pas besoin de mentionner explicitement la fonction Rollback, car celle-ci serait automatiquement annulée (sauf si elle était validée) au moment de sa cession.
  2. Mais si vous l'instanciez sans utiliser un bloc, dans ce cas, il est essentiel d'annuler la transaction en cas d'exception (précisément dans un bloc catch), ainsi qu'avec un contrôle nul pour un code plus robuste. Le fonctionnement de BeginTransaction diffère de la portée de la transaction (qui nécessite simplement une fonction complète si toutes les opérations ont été effectuées avec succès). Au lieu de cela, cela s'apparente au travail des transactions SQL.
4
roopaliv