Escopo da transação
A classe TransactionScope fornece uma maneira simples de marcar um bloco de código como participante da transação. Um escopo de transação pode selecionar e gerenciar automaticamente a transação do ambiente. Quando você cria uma instância de TransactionScope usando a instrução new, o gerenciador de transações determina qual transação deve participar. Se não ocorrer nenhuma exceção no escopo da transação (ou seja, entre a inicialização do objeto TransactionScope e a chamada do seu método Dispose), a transação da qual o escopo participa poderá continuar. Se ocorrer uma exceção no escopo da transação, a transação da qual ele participa será revertida. Quando o aplicativo conclui todo o trabalho a ser executado em uma transação, o método Complete é chamado uma vez. Isso informa ao gerenciador de transações que é aceitável confirmar a transação. Se o método Complete não for chamado, a transação será finalizada. Para obter mais informações sobre o escopo da transação, consulte a documentação do MSDN.
Implementação do escopo da transação no .NET Framework
O namespace System.Transactions faz parte do Microsoft .NET Framework versão 2.0, versões 3.0, 3.5 e 4. Ele fornece uma estrutura de transação totalmente integrada ao ADO.NET e integração de CLR (common language runtime) do SQL Server. A classe System.Transactions.transactionscope torna um bloco de códigos transacional inscrevendo implicitamente as conexões em uma transação distribuída. O método Complete pode ser chamado no fim do bloco de códigos marcado por TransactionScope. A transação será descontinuada se o método Complete não for chamado antes que seja feita uma chamada para o método Dispose. Se for gerada uma exceção, a transação será considerada descontinuada.
Para obter mais informações, consulte https://msdn2.microsoft.com/en-us/library/ms172070(VS.80).aspx.
Limitações de System.Transactions
O namespace System.Transactions não tem o suporte do .NET Compact Framework 2.0. Portanto, a implementação será apenas para o sistema operacional de área de trabalho do Windows e corresponderá ao .NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5 ou .NET Framework 4.
Observe que, se houver um tempo limite, a infraestrutura de System.Transactions chamará Rollback a partir de um thread separado. O thread principal não terá conhecimento de uma reversão que ocorrer no thread separado. As transações longas podem ter um comportamento não determinístico e cenários de confirmação parcial. Para resolver isso, aumente o período de tempo do objeto de escopo da transação ao criá-lo.
Em um escopo de transação, somente um objeto SqlCeConnection pode ser inscrito se nenhum outro gerenciador de transações já estiver inscrito no escopo da transação.
Se uma conexão estiver aberta fora de um escopo de transação e precisar ser inscrita em um escopo de transação existente, isso poderá ser feito com o método EnlistTransaction.
Implementação de TransactionScope
O SQL Server Compact é inscrito como um recurso da infraestrutura de System.Transactions.
Por padrão, quando vários comandos são abertos na conexão inscrita em um escopo de transação, eles são inscritos no contexto da transação atual. Também é possível abrir uma conexão que não esteja inscrita em um escopo de transação. Isso cria comandos que não estão inscritos. O tipo de transação padrão é serializável para transações do SQL Server Compact com escopo de transação.
Como as transações distribuídas não têm o suporte do SQL Server Compact, duas ou mais conexões não podem ser inscritas no mesmo escopo de transação, ou em um escopo de transação aninhada que compartilhe o mesmo escopo de transação de ambiente.
Não é permitida uma transação explícita para uma conexão inscrita dentro de um escopo de transação.
Não há suporte para inscrever implicitamente as conexões. Para inscrever um escopo de transação, siga este procedimento:
Abra uma conexão em um escopo de transação.
Ou, se a conexão já estiver aberta, chame o método EnlistTransaction no objeto de conexão.
Limitações do SQL Server Compact
Diferenças entre o SQL Server Compact e o SQL Server no que diz respeito ao escopo de transação:
Não há suporte para transações distribuídas no SQL Server Compact. Portanto, uma transação local não será automaticamente promovida para uma transação distribuível.
O SQL Server Compact dá suporte para transações paralelas, mesmo para vários conjuntos de resultados ativos, mas o SQL Server não dá suporte para transações paralelas.
Exemplo 1 de TransactionScope
O exemplo a seguir mostra como usar TransactionScope para inscrever e depois confirmar a transação.
using (TransactionScope transScope = new TransactionScope())
{
using (SqlCeConnection connection1 = new
SqlCeConnection(connectString1))
{
/* Opening connection1 automatically enlists it in the
TransactionScope as a lightweight transaction. */
connection1.Open();
// Do work in the connection.
}
// The Complete method commits the transaction.
transScope.Complete();
}
Exemplo 2 de TransactionScope
O exemplo a seguir mostra como usar TransactionScope para criar duas tabelas no banco de dados.
static void Setup(String strDbPath)
{
/* Delete the database file if it already exists. We will create a new one. */
if (File.Exists(strDbPath))
{
File.Delete(strDbPath);
}
// Create a new database.
SqlCeEngine engine = new SqlCeEngine();
engine.LocalConnectionString = @"Data source = " + strDbPath;
engine.CreateDatabase();
engine.Dispose();
}
/* This function creates two tables in the specified database. Before creating the tables, it re-creates the database.
These tables are created in a TransactionScope, which means that either both of them will be created or not created at all. */
static void CreateTablesInTransaction(String strDbPath)
{
/* Create the connection string. In order to have the connection enlisted into the TransactionScope, the Enlist property in the connection string must be explicitly set to true. */
String strConn = @"Data source = " + strDbPath + ";Enlist=true";
SqlCeConnection conn = new SqlCeConnection(strConn);
try
{
Setup(strDbPath); // Create a new database for our tables.
using (TransactionScope scope = new TransactionScope())
{
/* To enlist a connection into a TransactinScope, specify 'Enlist=true' in its connection string and open the connection in the scope of that TransactionScope object. */
conn.Open();
// Create the tables.
SqlCeCommand command = conn.CreateCommand();
command.CommandText = @"create table t1(col1 int)";
command.ExecuteNonQuery();
command.CommandText = @"create table t2(col1 int)";
command.ExecuteNonQuery();
/* If this statement is executed and the TransactionScope has not timed out, t1 and t2 will be created in the specified database. */
scope.Complete();
}
}
catch (SqlCeException e)
{
Console.WriteLine(e.Message);
}
finally
{
if (conn.State != System.Data.ConnectionState.Closed)
{
conn.Close();
}
conn.Dispose();
}
}
Exemplo 3 de TransactionScope
O exemplo a seguir mostra como os valores são inseridos em duas tabelas em um TransactionScope.
/* This function assumes that tables t1(col1 int) and t2(col1 int) are already created in the specified database. The condition for the following function is this:
If INSERTs into the first table succeed, then INSERT into the second table. However, if the INSERTs into the second table fail, roll back the inserts in the second table but do not roll back the inserts in the first table. Although this can also be done by way of regular transactions, this function demonstrates how to do it using TransactionScope objects. */
static void CreateTableAndInsertValues(String strDbPath)
{
/* Create the connection string. To have the connection enlisted into the TransactionScope, the Enlist property in the connection string must be explicitly set to true. */
String strConn = @"Data source = " + strDbPath + ";Enlist=true";
SqlCeConnection conn1 = new SqlCeConnection(strConn);
SqlCeConnection conn2 = new SqlCeConnection(strConn);
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
conn1.Open();
SqlCeCommand command1 = conn1.CreateCommand();
command1.CommandText = @"insert into t1(col1) values(1)";
command1.ExecuteNonQuery();
command1.CommandText = @"insert into t1(col1) values(2)";
command1.ExecuteNonQuery();
/* If this statement is executed and the TransactionScope has not timed out, two records will be inserted into table 1. */
scope.Complete();
try
{
using (TransactionScope scopeInner = new TransactionScope(TransactionScopeOption.RequiresNew))
{
conn2.Open();
SqlCeCommand command2 = conn2.CreateCommand();
command2.CommandText = @"insert into t2(col1) values(1)";
command2.ExecuteNonQuery();
command2.CommandText = @"insert into t2(col1) values(2)";
command2.ExecuteNonQuery();
/* If this statement is run and the TransactionScope has not timed out, two records will be inserted into table 2. */
scopeInner.Complete();
}
}
catch (SqlCeException e)
{
Console.WriteLine(@"Exception in Inner block: " + e.Message);
}
}
}
catch (SqlCeException e)
{
Console.WriteLine(@"Exception in Outer block: " + e.Message);
}
finally
{
// Close both the connections.
if (conn1.State != System.Data.ConnectionState.Closed)
{
conn1.Close();
}
if (conn2.State != System.Data.ConnectionState.Closed)
{
conn2.Close();
}
conn1.Dispose();
conn2.Dispose();
}
}