Ámbito de transacción
La clase TransactionScope proporciona una manera sencilla de marcar un bloque de código como participante de una transacción. Un ámbito de transacción puede seleccionar y administrar automáticamente la transacción ambiente. Al crear una instancia de la clase TransactionScope mediante la instrucción new, el administrador de transacciones determina en qué transacciones participar. Si no se produce ninguna excepción en el ámbito de la transacción (es decir, entre la inicialización del objeto TransactionScope y la llamada de su método Dispose), la transacción en que participa el ámbito puede continuar. Si aparece una excepción en el ámbito de la transacción, se deshará la transacción en la que participa. Una vez que la aplicación termina todo el trabajo que desea realizar en una transacción, se llama al método Complete una vez. Así se informa al administrador de transacciones de que se puede confirmar la transacción. Si no se llama al método Complete, finaliza la transacción. Para obtener más información sobre el ámbito de transacción, vea la documentación de MSDN.
Implementación del ámbito de las transacciones en .NET Framework
El espacio de nombres System.Transactions forma parte de Microsoft .NET Framework versión 2.0, versiones 3.0 y 3.5. Proporciona un marco de transacciones totalmente integrado con ADO.NET y la integración con Common Language Runtime (CLR) de SQL Server. La clase System.Transactions.transactionscope hace que un bloque de código sea transaccional enumerando implícitamente las conexiones de una transacción distribuida. Se puede llamar al método Complete al final del bloque de código marcado mediante TransactionScope. La transacción se detiene si no se llama al método Complete antes de hacer una llamada al método Dispose. Si se genera una excepción, se considera que la transacción se ha detenido.
Para obtener más información, vea https://msdn2.microsoft.com/en-us/library/ms172070(VS.80)..
Limitaciones de System.Transactions
El espacio de nombres System.Transactions no es compatible con .NET Compact Framework 2.0. Por lo tanto, la implementación será sólo para el sistema operativo de escritorio Windows y se corresponderá con NET Framework 2.0, .NET Framework 3.0 o .NET Framework 3.5.
Tenga en cuenta que si se agota el tiempo de espera, la infraestructura de System.Transactions llamará a Rollback desde otro subproceso. El subproceso principal no advertirá que se está produciendo una reversión en otro subproceso. Las transacciones largas podrían crear un comportamiento no determinista y situaciones de confirmaciones parciales. Para evitar este problema, aumente la duración del objeto de ámbito de transacción cuando lo cree.
En un ámbito de transacción sólo se puede inscribir un objeto SqlCeConnection si no hay otro administrador de transacciones ya inscrito en el ámbito.
Si se abre una conexión fuera de un ámbito de transacción y se tiene que inscribir en un ámbito de transacción existente, se puede hacer llamando al método EnlistTransaction.
Implementación de TransactionScope
SQL Server Compact 3.5 inscribe un recurso en la infraestructura System.Transactions.
De manera predeterminada, cuando se abren varios comandos en la conexión inscrita de un ámbito de transacción, se inscriben en el contexto de transacción actual. También se puede abrir una conexión que no está inscrita en un ámbito de transacción. Así se crean comandos que no están inscritos. El tipo de transacción predeterminado es serializable para las transacciones de SQL Server Compact 3.5 con ámbito de transacción.
Puesto que las transacciones distribuidas no son compatibles con SQL Server Compact 3.5, no se puede inscribir más de una conexión en el mismo ámbito de transacción, ni en un ámbito de transacción anidado que comparta el mismo ámbito de transacción ambiente.
No se admite una transacción explícita para una conexión inscrita dentro de un ámbito de transacción.
No se admite la inscripción implícita de conexiones. Para inscribir en un ámbito de transacción, puede hacer lo siguiente:
- Abrir una conexión en un ámbito de transacción.
- Si la conexión ya está abierta, puede llamar al método EnlistTransaction en el objeto de conexión.
Limitaciones de SQL Server Compact
Las diferencias entre SQL Server Compact 3.5 y SQL Server en relación con el ámbito de transacción son las siguientes:
- Las transacciones distribuidas no son compatibles con SQL Server Compact 3.5. En consecuencia, una transacción local no se promoverá automáticamente a transacción totalmente distribuible.
- Las transacciones paralelas son compatibles con SQL Server Compact 3.5, incluso para varios conjuntos de resultados activos, pero no son compatibles con SQL Server.
Ejemplo 1 de TransactionScope
En el siguiente ejemplo se muestra cómo usar TransactionScope para inscribir la transacción y, después, confirmarla.
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();
}
Ejemplo 2 de TransactionScope
En el siguiente ejemplo se muestra cómo usar TransactionScope para crear dos tablas en la base de datos.
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();
}
}
Ejemplo 3 de TransactionScope
En el siguiente ejemplo se muestra cómo se insertan valores en dos tablas en una clase 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();
}
}