검색결과 리스트
2007/12/23 에 해당되는 글 1건
- 2007.12.23 [가이드]System.Transaction의 활용 (1)
1차 설계때 포함시켰던 Enterprise Library를 배제하기로 결정했습니다.
Enterprise Library는 일단 그 자체만으로도 상당히 버겁고 또 사용하지 않는 다수의 Class도
함께 가지고 가야 한다는 점 그리고 프레임웍 버전업때의 자유도 등을 고려해서
이번 작업에는 적절하지 않다는 판단을 하게 되었습니다.
그리고 기본적으로 System.EnterpriseService를 사용하는 것을 표준으로 채택하려고 했으나
닷넷 프레임웍 2.0에 새로 추가된 System.Transaction을 활용하는 것도 표준으로 함께 권장한다.
System.Transaction을 우선적으로 사용하고 필요한 곳에는 System.EnterpriseService를
함께 사용할 수 있도록 했습니다.
자료들을 찾아보면 대체적으로 System.Transaction이 좀더 가볍고 유연한 방법이라고 되어 있는
것 같습니다. 아무래도 새버전에 맞쳐서 나온것 이라서 그런것인지 모르겠습니다만
아무튼 우리도 System.Transactions을 우선적으로 사용하는 것으로 결정했습니다.
또 System.Transactions의 경우 SQL Server, ADO.NET, MSMQ, MSDTC를 모두 지원하는
유연함과 강력함을 가지고 있습니다.
System.Transactions의 경우 암시적으로 특정범위안의 모든 Database Handling을 하나의
Transaction으로 묶어서 처리할 수 있는데 <리스트1>소스에서 사용법을 볼 수 있습니다.
// This function takes arguments for 2 connection strings and commands to create a transaction // involving two SQL Servers. It returns a value > 0 if the transaction is committed, 0 if the // transaction is rolled back. To test this code, you can connect to two different databases // on the same server by altering the connection string, or to another RDBMS such as Oracle // by altering the code in the connection2 code block. static public int CreateTransactionScope( string connectString1, string connectString2, string commandText1, string commandText2) { // Initialize the return value to zero and create a StringWriter to display results. int returnValue = 0; System.IO.StringWriter writer = new System.IO.StringWriter(); // Create the TransactionScope to execute the commands, guaranteeing // that both commands can commit or roll back as a single unit of work. using (TransactionScope scope = new TransactionScope()) { using (SqlConnection connection1 = new SqlConnection(connectString1)) { try { // Opening the connection automatically enlists it in the // TransactionScope as a lightweight transaction. connection1.Open(); // Create the SqlCommand object and execute the first command. SqlCommand command1 = new SqlCommand(commandText1, connection1); returnValue = command1.ExecuteNonQuery(); writer.WriteLine("Rows to be affected by command1: {0}", returnValue); // If you get here, this means that command1 succeeded. By nesting // the using block for connection2 inside that of connection1, you // conserve server and network resources as connection2 is opened // only when there is a chance that the transaction can commit. using (SqlConnection connection2 = new SqlConnection(connectString2)) try { // The transaction is escalated to a full distributed // transaction when connection2 is opened. connection2.Open(); // Execute the second command in the second database. returnValue = 0; SqlCommand command2 = new SqlCommand(commandText2, connection2); returnValue = command2.ExecuteNonQuery(); writer.WriteLine("Rows to be affected by command2: {0}", returnValue); } catch (Exception ex) { // Display information that command2 failed. writer.WriteLine("returnValue for command2: {0}", returnValue); writer.WriteLine("Exception Message2: {0}", ex.Message); } } catch (Exception ex) { // Display information that command1 failed. writer.WriteLine("returnValue for command1: {0}", returnValue); writer.WriteLine("Exception Message1: {0}", ex.Message); } } // The Complete method commits the transaction. If an exception has been thrown, // Complete is not called and the transaction is rolled back. scope.Complete(); } // The returnValue is greater than 0 if the transaction committed. if (returnValue > 0) { writer.WriteLine("Transaction was committed."); } else { // You could write additional business logic here, for example, you can notify the caller // by throwing a TransactionAbortedException, or logging the failure. writer.WriteLine("Transaction rolled back."); } // Display messages. Console.WriteLine(writer.ToString()); return returnValue; } <리스트1> 트랜젹션 범위를 사용하여 암시적 트랜잭션을 구현하는 예
새로운 TransactionScope 객체를 만들면 Transaction Scope가 시작됩니다. 위에서 볼 수 있는데로
using문장을 이용해서 사용범위를 한정해 주는게 좋습니다.
Transaction을 확정하기 위해서는 TransactionSope객체의 Complete()호출해야 합니다.
Complete()을 호출하지 않으면 Transaction이 완료되지 않고 Rollback되어 버립니다.
using문장을 이용해서 TransactionScope를 설정할 경우에 어떤 예외가 발생하면
TranscationScope의 Dispose()가 호출됩니다. TransactionScope의 경우 Dispose()가 호출되면
Transaction의 끝으로 간주해서 해당 Tracation의 상태에 따라서 확정이나 취소를 선택하게 됩니다.
void RootMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ SomeMethod(); scope.Complete(); } } void SomeMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ scope.Complete(); } }<리스트2> 트랜젝션이 중첩
<리스트2>에서 처럼 Transcation을 중첩할 수도 있습니다.
이렇게 중첩했을 경우에는 모든 Transcation이 확정되지 않으면 일괄 취소됩니다.
TrancationOption을 사용했을 경우에는 옵션에 따라서 세가지로 동작이 달라집니다.
Required를 사용하여 범위를 인스턴스화하고 앰비언트 트랜잭션이 있으면 범위는 이 트랜잭션에 참여합니다.
그러나 앰비언트 트랜잭션이 없으면 범위는 새 트랜잭션을 만들고 루트 범위가 됩니다.
이 값은 기본값입니다. Required를 사용하면 범위 내부의 코드는 루트 범위이거나 아니면 단순히 앰비언트 트랜잭션에 참여하는지에 따라 다르게 동작할 필요가 없습니다. 이 코드는
두 경우에 모두 동일하게 작동해야 합니다.RequiresNew를 사용하여 범위를 인스턴스화하면 이 범위는 항상 루트 범위가 됩니다. 이 범위가 새 트랜잭션을
시작하면 해당 트랜잭션은 범위 내부의 새 앰비언트 트랜잭션이 됩니다.Suppress를 사용하여 범위를 인스턴스화하면 앰비언트 트랜잭션이 있는지 여부에 관계없이 이 범위는
트랜잭션에 참여하지 않습니다. 이 값을 사용하여 인스턴스화된 범위에서는 항상 앰비언트
트랜잭션이 null입니다.
이 외에도 시간 제한이나 격리수준을 설정하는 방법으로 세세한 설정이 가능합니다.
자세한 내용은 http://msdn2.microsoft.com/ko-kr/library/ms172152(VS.80).aspx 을
참조하기 바랍니다.
한가지 TransactionScope의 결정적 시간 제한 옵션은 따로 있습니다.
machine.config에서 MachineSettingsSection에서 전체적인 설정이 이미 설정되어 있습니다.
따라서 여러분이 하는 설정도 machine.config의 제한 시간을 넘어가지 못합니다.
http://msdn2.microsoft.com/ko-kr/library/ms172152(VS.80).aspx
기본 타임아웃이 10분으로 잡혀 있다고 합니다. 따라서 여러분들이 시간을 아무리 늘려도
10분 이상을 설정할 수 없습니다. 만약 더 큰 시간이 필요하다면 machine.config의 내용을
수정해야 합니다
<system.transactions>
<defaultSettings distributedTransactionManagerName="NERPSVR" timeout="00:00:00" />
<machineSettings maxTimeout="00:00:00" />
</system.transactions>
<리스트3>Machine.config의 Transaction Timeout 설정
이 내용은 정성태님의 블로그에서 내용을 참조할 수 있습니다.
http://www.sysnet.pe.kr/Default.aspx?mode=3&sub=0&pageno=0&detail=1&wid=716
댓글을 달아 주세요
구체적인 장점을 모르고 사용해왔는데... 여러가지 많은 정보를 알게되었네요 좋은 정보 감사합니다 ^^