Étendue de la transaction
La classe TransactionScope permet de marquer facilement un bloc de code comme participant à une transaction. Une étendue de transaction peut sélectionner et manager automatiquement la transaction ambiante. Lorsque vous instanciez une classe TransactionScope à l'aide de l'instruction new, le gestionnaire de transactions détermine quelles sont les transactions auxquelles participer. Si aucune exception ne se produit dans l'étendue de la transaction (c'est-à-dire entre l'initialisation de l'objet TransactionScope et l'appel à sa méthode Dispose), la transaction à laquelle l'étendue participe peut continuer. Si une exception se produit dans l'étendue de la transaction, la transaction à laquelle elle participe sera restaurée. Lorsque l'application a terminé toutes les exécutions prévues dans une transaction, la méthode Complete est appelée une fois. Elle informe le gestionnaire de transactions que la transaction peut être validée. Si la méthode Complete n'est pas appelée, la transaction se termine. Pour plus d'informations sur l'étendue des transactions, consultez la Documentation MSDN.
Implémentation de l'étendue de la transaction dans le .NET Framework
L'espace de noms System.Transactions est inclus dans Microsoft .NET Framework version 2.0, versions 3.0, 3.5 et 4. Il fournit une infrastructure de transaction qui s'intègre parfaitement au Common Language Runtime de ADO.NET et SQL Server. La classe System.Transactions.transactionscope rend un bloc de code transactionnel en inscrivant implicitement les connexions dans une transaction distribuée. La méthode Complete peut être appelée à la fin du bloc de code marqué par TransactionScope. La transaction est obsolète si la méthode Complete n'est pas appelée avant qu'un appel à la méthode Dispose ne soit effectué. Si une exception est levée, la transaction est considérée comme obsolète.
Pour plus d'informations, consultez https://msdn2.microsoft.com/en-us/library/ms172070(VS.80).aspx.
Limitations de System.Transactions
L'espace de noms System.Transactions n'est pas pris en charge par le .NET Compact Framework 2.0. Par conséquent, l'implémentation n'est possible que pour les systèmes d'exploitation Windows et correspondra à .NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5 ou .NET Framework 4.
Notez qu'en cas de délai d'attente, l'infrastructure System.Transactions appellera une restauration depuis un thread séparé. Le thread principal ne sera pas à connaissance de la restauration en cours dans l'autre thread. Dans les transactions longues on pourra constater un comportement non déterminable et des scénarios de validation partielle. Pour résoudre cela, augmentez la valeur Timespan de l'objet d'étendue de transaction lorsque vous créez l'objet.
Dans une étendue de transaction, un seul objet SqlCeConnection peut être inscrit au sein de l'étendue.
Si une connexion est ouverte en-dehors d'une étendue de transaction et doit être inscrite dans une étendue de transaction existante, il est possible d'utiliser la méthode EnlistTransaction.
Implémentation de TransactionScope
SQL Server Compact s'inscrit en tant que ressource auprès de l'infrastructure System.Transactions.
Par défaut, lorsque plusieurs commandes sont ouvertes sur une connexion inscrite dans une étendue de transaction, elles sont inscrites auprès du contexte de transaction actuel. Il est également possible d'ouvrir une connexion non inscrite dans une étendue de transaction. Cela crée des commandes non inscrites. Le type de transaction par défaut est sérialisable pour les transactions SQL Server Compact avec étendue.
Étant donné que les transactions distribuées ne sont pas prises en charge par SQL Server Compact, il ne sera pas possible d'inscrire deux connexions ou plus dans la même étendue de transaction ou dans une étendue de transaction imbriquée qui partage la même étendue de transaction ambiante.
Une transaction explicite pour une connexion inscrite dans une étendue de transaction n'est pas autorisée.
L'inscription implicite de connexions n'est pas prise en charge. Pour l'inscription dans une étendue de transaction, vous pouvez :
ouvrir une connexion dans une étendue de transaction,
ou, si la connexion est déjà ouverte, appeler la méthode EnlistTransaction sur l'objet de connexion.
Limitations de SQL Server Compact
Les différences entre SQL Server Compact et SQL Server en ce qui concerne l'étendue de la transaction sont les suivantes :
Les transactions distribuées ne sont pas prises en charge par SQL Server Compact. Par conséquent, une transaction locale n’est pas automatiquement promue à une transaction totalement distribuable.
Les transactions parallèles sont prises en charge par SQL Server Compact, même pour MARS (Multiple Active Result Sets) mais ne sont pas prise en charge par SQL Server.
TransactionScope – Exemple 1
L'exemple suivant montre comment utiliser TransactionScope pour inscrire, puis valider la transaction.
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();
}
TransactionScope – Exemple 2
L'exemple de code suivant montre comment utiliser TransactionScope pour créer deux tables dans la base de données.
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();
}
}
TransactionScope – Exemple 3
L'exemple de code suivant montre comment insérer les valeurs dans deux tables, dans 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();
}
}