Como o TransactionScope reverte as transações?

99

Estou escrevendo um teste de integração onde irei inserir vários objetos em um banco de dados e, em seguida, verificar se meu método recupera esses objetos.

Minha conexão com o banco de dados é através do NHibernate ... e meu método usual de criar tal teste seria fazer o seguinte:

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

No entanto, descobri recentemente sobre o TransactionScope, que aparentemente pode ser usado para essa finalidade ...

Alguns exemplos de código que encontrei são os seguintes:

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;

}

Acredito que se não incluir a linha txScope.Complete()que os dados inseridos serão revertidos. Mas, infelizmente, eu não entendo como isso é possível ... como é que o txScopeobjeto manter uma faixa da deptAdaptere empAdapterobjetos e suas transações no banco de dados.

Sinto que estou faltando um pouco de informação aqui ... sou realmente capaz de substituir minhas chamadas BeginTransaction()e RollbackTransaction() cercando meu código usando TransactionScope?

Se não, como TransactionScopefunciona para reverter as transações?

mezóide
fonte
Nunca usei o NHibernate, mas talvez este link te ajude.
Se você está procurando uma maneira melhor de gerenciar suas sessões do NHibernate para agrupar operações em transações, você pode querer verificar minha postagem no blog sobre esse tópico dotnetchris.wordpress.com/2009/01/27/…
Chris Marisic

Respostas:

107

Essencialmente, o TransactionScope não rastreia o seu adaptador, o que ele faz é rastrear conexões de banco de dados. Quando você abre uma conexão de banco de dados, as conexões verificarão se há uma transação ambiente (Escopo da transação) e se houver, se alistarão nela. Cuidado, se houver mais de uma conexão com o mesmo servidor SQL, isso será escalado para uma Transação Distribuída.

O que acontece, já que você está usando um bloco de uso e está garantindo que dispose será chamado mesmo se ocorrer uma exceção. Portanto, se dispose for chamado antes de txScope.Complete (), o TransactionScope dirá às conexões para reverter suas transações (ou o DTC).

JoshBerke
fonte
10
O TransactionScope não rastreia nada além da transação atual no thread e a modifica se for necessário com base no modelo (requer, requer novo, etc, etc). A transação simplesmente notifica qualquer coisa que esteja relacionada a ela, não apenas as conexões de banco de dados.
casperOne
1
Eu acho que isso não é totalmente verdade. Fiz um rastreamento parcial do código-fonte do TransactionScope e também vi este msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx que diz "Se ele não criou a transação, a confirmação ocorre sempre que Commit é chamado pelo proprietário do objeto CommittableTransaction. Nesse ponto, o Gerenciador de transações chama os gerenciadores de recursos e os informa para confirmar ou reverter, com base no fato de o método Complete ter sido chamado no objeto TransactionScope. " O rastreamento de origem também indica esse comportamento.
user44298
Achei este SO Q&A útil: IEnlistmentNotification
mungflesh
Ainda não está claro para mim. Parece que os objetos com métodos chamados dentro do escopo da transação devem ser cooperativos com o mecanismo de transação. Eles devem procurar notificações de confirmação / reversão pelo sistema e serem capazes de reverter por conta própria, uma vez que são eles que executam a reversão quando necessário (se uma exclusão de arquivo foi executada, obviamente não podemos revertê-la magicamente, a menos que tomemos alguma medida de precaução). Também parece que as operações do servidor SQL são cooperativas. Mas existe algum outro objeto cooperativo em .Net? E como escrever uma aula cooperativa? Documentação?
minutos
54

A TransactionScopeclasse trabalha com a Transactionclasse , que é específica do segmento.

Quando o TransactionScopeé criado, ele verifica se há um Transactionpara o segmento; se houver um, ele o usa; caso contrário, cria um novo e o coloca na pilha.

Se ele usa um existente, ele apenas incrementa um contador para lançamentos (já que você tem que chamá Dispose-lo). No último lançamento, se Transactionnão for comprometido, ele reverte todo o trabalho.

Quanto ao motivo pelo qual as classes parecem saber magicamente sobre as transações, isso é deixado como um detalhe de implementação para as classes que desejam trabalhar com este modelo.

Quando você cria suas instâncias deptAdaptere emptAdapter, eles verificam se há uma transação atual no encadeamento (a Currentpropriedade estática na Transactionclasse). Se houver, ele se registra com o Transaction, para participar da sequência de confirmação / reversão (que Transactioncontrola e pode se propagar para vários coordenadores de transação, como kernel, distribuído, etc.).

casperOne
fonte
4
Como funciona com SqlConnection (s) criadas dentro do escopo? A classe SqlConnection usa internamente a classe Transaction que, por sua vez, se inscreve no TransactionScope? Ou ele se inscreve diretamente no TLS?
Kakira