트랜잭션 범위

TransactionScope 클래스를 사용하면 특정 코드 블록을 트랜잭션에 참여하는 코드 블록으로 간단하게 표시할 수 있습니다. 트랜잭션 범위는 주변 트랜잭션을 자동으로 선택하고 관리합니다. new 문을 사용하여 TransactionScope를 인스턴스화하면 트랜잭션 관리자는 참여할 트랜잭션을 결정합니다. 트랜잭션 범위(즉, TransactionScope 개체의 초기화와 해당 Dispose 메서드의 호출 사이)에서 예외가 발생하지 않으면 범위가 참여하는 트랜잭션이 지속될 수 있습니다. 트랜잭션 범위에서 예외가 발생하면 범위가 참여하는 트랜잭션이 롤백됩니다. 응용 프로그램이 트랙잭션에서 수행하고자 하는 모든 작업을 완료하면 Complete 메서드가 한 번 호출됩니다. 그러면 트랜잭션 관리자가 트랜잭션을 커밋할 수 있습니다. Complete 메서드가 호출되지 않으면 트랜잭션이 종료됩니다. 트랜잭션 범위에 대한 자세한 내용은 MSDN 설명서를 참조하십시오.

.NET Framework에서 트랜잭션 범위 구현

System.Transactions 네임스페이스는 Microsoft .NET Framework 버전 2.0, 버전 3.0, 3.5 및 4에 포함되어 있으며, ADO.NET 및 SQL Server CLR(공용 언어 런타임) 통합과 완전하게 통합되는 트랜잭션 프레임워크를 제공합니다. System.Transactions.transactionscope 클래스는 암시적으로 연결을 분산 트랜잭션에 등록하여 코드 블록을 트랜잭션으로 만듭니다. TransactionScope로 표시되는 코드 블록의 끝에서는 Complete 메서드를 호출할 수 있습니다. Complete 메서드가 Dispose 메서드보다 먼저 호출되지 않으면 트랜잭션이 중단됩니다. 예외가 발생하면 트랜잭션이 중단된 것으로 간주됩니다.

자세한 내용은 https://msdn2.microsoft.com/ko-kr/library/ms172070(VS.80).aspx를 참조하십시오.

System.Transactions 제한 사항

System.Transactions 네임스페이스는 .NET Compact Framework 2.0에서 지원하지 않습니다. 따라서 구현은 Windows 데스크톱 운영 체제용으로만 제공되며 .NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5 또는 .NET Framework 4와 작동합니다.

제한 시간이 있을 경우 System.Transactions 인프라가 별도의 스레드에서 롤백을 호출합니다. 주 스레드는 별도의 스레드에서 발생하는 롤백을 인식하지 못합니다. 긴 트랜잭션의 경우 비결정적 동작과 부분적인 커밋 시나리오가 발생할 수 있습니다. 이를 해결하려면 개체를 만들 때 트랜잭션 범위 개체의 시간 범위를 늘리십시오.

트랜잭션 범위에서는 트랜잭션 범위에 이미 등록된 다른 트랜잭션 관리자가 없는 경우 한 개의 SqlCeConnection 개체만 등록될 수 있습니다.

연결이 트랜잭션 범위 밖에서 열렸는데 이 연결을 기존 트랜잭션 범위에 등록해야 하는 경우 EnlistTransaction 메서드를 사용하면 됩니다.

TransactionScope 구현

SQL Server Compact는 리소스를 System.Transactions 인프라에 등록합니다.

기본적으로 트랜잭션 범위 내에 있는 등록된 연결에서 여러 개의 명령이 열리는 경우 이들은 현재 트랜잭션 컨텍스트에 등록됩니다. 트랜잭션 범위에 등록되지 않은 연결도 열 수 있습니다. 그러면 등록되지 않은 명령이 생깁니다. 트랜잭션 범위가 있는 SQL Server Compact 트랜잭션의 기본 유형은 직렬화 가능 트랜잭션입니다.

분산 트랜잭션은 SQL Server Compact에서 지원하지 않으므로 둘 이상의 연결을 동일한 트랜잭션 범위에 등록하거나 동일한 주변 트랜잭션 범위를 공유하는 중첩 트랜잭션 범위에 등록할 수 없습니다.

트랜잭션 범위 내에 있는 등록된 연결에 대한 명시적 트랜잭션은 허용되지 않습니다.

암시적으로 등록된 연결은 지원되지 않습니다. 트랜잭션 범위에 등록하려면 다음을 수행하십시오.

  • 트랜잭션 범위 내에 있는 연결을 엽니다.

  • 또는 연결이 이미 열린 경우 연결 개체에 대해 EnlistTransaction 메서드를 호출합니다.

SQL Server Compact 제한 사항

트랜잭션 범위와 관련하여 SQL Server Compact와 SQL Server의 차이점은 다음과 같습니다.

  • SQL Server Compact에서는 분산 트랜잭션이 지원되지 않습니다. 따라서 로컬 트랜잭션이 완전히 분산 가능한 트랜잭션으로 자동 승격되지 않습니다.

  • SQL Server Compact에서는 활성 결과 집합이 여러 개인 경우에도 병렬 트랜잭션이 지원되지만 SQL Server에서는 병렬 트랜잭션이 지원되지 않습니다.

TransactionScope 예 1

다음 예는 TransactionScope를 사용하여 트랜잭션을 등록한 다음 커밋하는 방법을 보여 줍니다.

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 예 2

다음 예는 TransactionScope를 사용하여 데이터베이스에 두 개의 테이블을 만드는 방법을 보여 줍니다.

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 예 3

다음 예는 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();

}

}