Comment TransactionScope annule-t-il les transactions?

100

J'écris un test d'intégration dans lequel j'insérerai un certain nombre d'objets dans une base de données, puis je vérifierai si ma méthode récupère ces objets.

Ma connexion à la base de données se fait via NHibernate ... et ma méthode habituelle pour créer un tel test serait de faire ce qui suit:

NHibernateSession.BeginTransaction();

//use nhibernate to insert objects into database
//retrieve objects via my method
//verify actual objects returned are the same as those inserted

NHibernateSession.RollbackTransaction();

Cependant, j'ai récemment découvert TransactionScope qui peut apparemment être utilisé à cette fin ...

Un exemple de code que j'ai trouvé est le suivant:

public static int AddDepartmentWithEmployees(Department dept)
{

    int res = 0;

    DepartmentAdapter deptAdapter = new DepartmentAdapter();
    EmployeeAdapter empAdapter = new EmployeeAdapter();
    using (TransactionScope txScope = new TransactionScope())
    {

        res += deptAdapter.Insert(dept.DepartmentName);
        //Custom method made to return Department ID 
        //after inserting the department "Identity Column"
        dept.DepartmentID = deptAdapter.GetInsertReturnValue();
        foreach(Employee emp in dept.Employees)
        {

            emp.EmployeeDeptID = dept.DepartmentID;
            res += empAdapter.Insert(emp.EmployeeName, emp.EmployeeDeptID);

        }
        txScope.Complete();

    }
    return res;

}

Je crois que si je n'inclus pas la ligne txScope.Complete(), les données insérées seront annulées. Mais malheureusement , je ne comprends pas comment cela est possible ... comment l' txScopeobjet garder une trace du deptAdapteret des empAdapterobjets et de leurs transactions sur la base de données.

J'ai l'impression qu'il me manque un peu d'informations ici ... Suis-je vraiment capable de remplacer mes appels BeginTransaction()et RollbackTransaction() en entourant mon code en utilisant TransactionScope?

Sinon, comment TransactionScopefonctionne-t-il pour annuler les transactions?

mezoid
la source
Je n'ai jamais utilisé NHibernate, mais peut - être que ce lien vous aidera.
Si vous cherchez un meilleur moyen de gérer vos sessions NHibernate pour regrouper les opérations en transactions, vous voudrez peut-être consulter mon article de blog sur ce sujet dotnetchris.wordpress.com/2009/01/27
Chris Marisic

Réponses:

108

Essentiellement, TransactionScope ne suit pas votre adaptateur, il suit les connexions à la base de données. Lorsque vous ouvrez une connexion DB, les connexions recherchent s'il existe une transaction ambiante (Transaction Scope) et si c'est le cas, s'enrôlent avec elle. Attention, s'il existe plusieurs connexions au même serveur SQL, cela se transformera en une transaction distribuée.

Que se passe-t-il puisque vous utilisez un bloc using, vous vous assurez que dispose sera appelé même si une exception se produit. Donc, si dispose est appelé avant txScope.Complete (), le TransactionScope indiquera aux connexions d'annuler leurs transactions (ou le DTC).

JoshBerke
la source
10
TransactionScope ne suit rien d'autre que la Transaction actuelle sur le thread et la modifie si nécessaire en fonction du modèle (nécessite, nécessite un nouveau, etc., etc.). La transaction notifie simplement tout ce qui s'inscrit avec elle, pas seulement les connexions à la base de données.
casperOne
1
Je pense que ce n'est pas entièrement vrai. J'ai fait une trace partielle du code source de TransactionScope et et j'ai également vu ce msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx qui dit "S'il n'a pas créé la transaction, la validation se produit chaque fois que Commit est appelée par le propriétaire de l'objet CommittableTransaction. À ce stade, le gestionnaire de transactions appelle les gestionnaires de ressources et les informe de la validation ou de l'annulation, selon que la méthode Complete a été appelée sur l'objet TransactionScope. " La trace source indique également ce comportement.
user44298
J'ai trouvé ce Q&R SO utile: IEnlistmentNotification
mungflesh
Toujours pas clair pour moi. Il semble que les objets avec des méthodes appelées dans la portée de la transaction doivent coopérer avec le mécanisme de transaction. Ils doivent rechercher les notifications de validation / annulation par le système et être en mesure de revenir en arrière car ce sont eux qui effectuent la restauration lorsque cela est nécessaire (si une suppression de fichier a été exécutée, nous ne pouvons évidemment pas l'annuler par magie à moins que nous ayons pris une certaine mesure de précaution). Il semble également que les opérations du serveur SQL soient coopératives. Mais y a-t-il un autre objet coopératif dans .Net? Et comment écrire une classe coopérative? Documentation?
mn
54

La TransactionScopeclasse travaille avec la Transactionclasse , qui est spécifique au thread.

Lorsque le TransactionScopeest créé, il vérifie s'il existe un Transactionpour le thread; s'il en existe un, il l'utilise, sinon, il en crée un nouveau et le pousse sur la pile.

S'il en utilise un existant, il incrémente simplement un compteur pour les versions (puisque vous devez l'appeler Dispose). Sur la dernière version, si le Transactionn'était pas engagé, il annule tout le travail.

Quant à savoir pourquoi les classes semblent savoir par magie sur les transactions, cela reste un détail d'implémentation pour les classes qui souhaitent travailler avec ce modèle.

Lorsque vous créez vos instances deptAdapteret emptAdapter, ils vérifient s'il existe une transaction en cours sur le thread (la Currentpropriété statique de la Transactionclasse). Si tel est le cas, il s'enregistre auprès du Transaction, pour prendre part à la séquence de validation / annulation (qui Transactioncontrôle et pourrait se propager à différents coordinateurs de transaction, tels que le noyau, distribué, etc.).

casperOne
la source
4
Comment cela fonctionne-t-il avec les SqlConnection (s) créées dans la portée? La classe SqlConnection utilise-t-elle en interne la classe Transaction qui s'inscrit à son tour avec TransactionScope? Ou s'inscrit-il directement dans le TLS?
Kakira