[SQL] 하지 액세스 SqlTransaction 객체는 catch 블록에서 롤백 할 수
SQL하지 액세스 SqlTransaction 객체는 catch 블록에서 롤백 할 수
나는 문제가있어, 모든 기사 또는 예 나는 그것에 대해 걱정하지 보인다 발견했다.
나는 거래에서 일부 데이터베이스 작업을 수행 할. 내가 뭘 원하는 대부분의 예와 매우 유사합니다 :
using (SqlConnection Conn = new SqlConnection(_ConnectionString))
{
try
{
Conn.Open();
SqlTransaction Trans = Conn.BeginTransaction();
using (SqlCommand Com = new SqlCommand(ComText, Conn))
{
/* DB work */
}
}
catch (Exception Ex)
{
Trans.Rollback();
return -1;
}
}
그러나 문제는 SqlTransaction 트랜스는 try 블록 내에서 선언 된 것입니다. 그래서 캐치 () 블록 accessable 한하지 않습니다. 대부분의 예는 try 블록 전에 Conn.Open ()와 Conn.BeginTransaction ()를 수행하지만 모두가 여러 예외를 던질 수 있기 때문에 즉, 조금 위험한 생각합니다.
암 I의 잘못은, 또는 대부분의 사람들은 이러한 위험을 무시합니까? 가장 좋은 방법은 예외가 발생하는 경우, 롤백 할 수 있도록 무엇입니까?
해결법
-
==============================
1.
using (var Conn = new SqlConnection(_ConnectionString)) { SqlTransaction trans = null; try { Conn.Open(); trans = Conn.BeginTransaction(); using (SqlCommand Com = new SqlCommand(ComText, Conn, trans)) { /* DB work */ } trans.Commit(); } catch (Exception Ex) { if (trans != null) trans.Rollback(); return -1; } }
또는 당신도 깨끗하고 쉽게 이동이를 사용할 수 있습니다 :
using (var Conn = new SqlConnection(_ConnectionString)) { try { Conn.Open(); using (var ts = new System.Transactions.TransactionScope()) { using (SqlCommand Com = new SqlCommand(ComText, Conn)) { /* DB work */ } ts.Complete(); } } catch (Exception Ex) { return -1; } }
-
==============================
2.나는 그리, 유형을 입력하고 null로 변수를 설정 좋아한다 :
나는 그리, 유형을 입력하고 null로 변수를 설정 좋아한다 :
try { using (var conn = new SqlConnection(/* connection string or whatever */)) { conn.Open(); using (var trans = conn.BeginTransaction()) { try { using (var cmd = conn.CreateCommand()) { cmd.Transaction = trans; /* setup command type, text */ /* execute command */ } trans.Commit(); } catch (Exception ex) { trans.Rollback(); /* log exception and the fact that rollback succeeded */ } } } } catch (Exception ex) { /* log or whatever */ }
당신이 MySQL을하거나 다른 업체로 전환하고자한다면, 당신은 단 1 줄을 수정해야 할 것이다.
-
==============================
3.이것을 사용
이것을 사용
using (SqlConnection Conn = new SqlConnection(_ConnectionString)) { SqlTransaction Trans = null; try { Conn.Open(); Trans = Conn.BeginTransaction(); using (SqlCommand Com = new SqlCommand(ComText, Conn)) { /* DB work */ } } catch (Exception Ex) { if (Trans != null) Trans.Rollback(); return -1; } }
BTW - 당신은 성공적으로 처리하는 경우이를 저지하지 않았다
-
==============================
4.
using (SqlConnection Conn = new SqlConnection(_ConnectionString)) { try { Conn.Open(); SqlTransaction Trans = Conn.BeginTransaction(); try { using (SqlCommand Com = new SqlCommand(ComText, Conn)) { /* DB work */ } } catch (Exception TransEx) { Trans.Rollback(); return -1; } } catch (Exception Ex) { return -1; } }
-
==============================
5.마이크로 소프트 샘플은이 MSDN의 링크를 참조 시도 / 캐치의 트랜스 외부를 시작 놓습니다. 내가 들어 BeginTransaction 방법 중 하나 예외를 던지거나 트랜잭션을 시작하지만 결코 모두 (문서는 이것이 불가능 말을하지 않지만)한다고 가정합니다.
마이크로 소프트 샘플은이 MSDN의 링크를 참조 시도 / 캐치의 트랜스 외부를 시작 놓습니다. 내가 들어 BeginTransaction 방법 중 하나 예외를 던지거나 트랜잭션을 시작하지만 결코 모두 (문서는 이것이 불가능 말을하지 않지만)한다고 가정합니다.
그러나, 당신은 더 나은 당신을 위해의 많은 (그렇지 않은) 무거운를 관리하여 TransactionScope를 사용 할 수있다 :이 링크
-
==============================
6.
SqlConnection conn = null; SqlTransaction trans = null; try { conn = new SqlConnection(_ConnectionString); conn.Open(); trans = conn.BeginTransaction(); /* * DB WORK */ trans.Commit(); } catch (Exception ex) { if (trans != null) { trans.Rollback(); } return -1; } finally { if (conn != null) { conn.Close(); } }
-
==============================
7.나는이 질문에게 2018의 처음 끝을 발견했을 때 나는 대답을 투표 한 다음 상단에 버그가있을 수 있다고 생각하지 못했지만, 거기 간다. 내가 먼저 간단하게 대답을 주석에 대해 생각하지만 다시 난 내 자신의 참조 내 주장을 백업하고 싶었다. 그리고 테스트 I 않았다 (닷넷 프레임 워크 4.6.1 및 닷넷 코어 2.1을 기반으로.)
나는이 질문에게 2018의 처음 끝을 발견했을 때 나는 대답을 투표 한 다음 상단에 버그가있을 수 있다고 생각하지 못했지만, 거기 간다. 내가 먼저 간단하게 대답을 주석에 대해 생각하지만 다시 난 내 자신의 참조 내 주장을 백업하고 싶었다. 그리고 테스트 I 않았다 (닷넷 프레임 워크 4.6.1 및 닷넷 코어 2.1을 기반으로.)
영업의 제약 조건을 감안할 때, 트랜잭션은 이미 다른 답변에서 언급 한 2 개의 다른 구현에 우리를 잎 연결 내에서 선언되어야한다 :
TransactionScope에 사용
using (SqlConnection conn = new SqlConnection(conn2)) { try { conn.Open(); using (TransactionScope ts = new TransactionScope()) { conn.EnlistTransaction(Transaction.Current); using (SqlCommand command = new SqlCommand(query, conn)) { command.ExecuteNonQuery(); //TESTING: throw new System.InvalidOperationException("Something bad happened."); } ts.Complete(); } } catch (Exception) { throw; } }
SqlTransaction을 사용하여
using (SqlConnection conn = new SqlConnection(conn3)) { try { conn.Open(); using (SqlTransaction ts = conn.BeginTransaction()) { using (SqlCommand command = new SqlCommand(query, conn, ts)) { command.ExecuteNonQuery(); //TESTING: throw new System.InvalidOperationException("Something bad happened."); } ts.Commit(); } } catch (Exception) { throw; } }
대신 당신이 conn.EnlistTransaction에 명시 적으로 입대해야하는도록 SqlConnection 내에서 TransactionScope에를 선언 할 때 연결 개체가 자동으로 트랜잭션에 입대되지 않는다는 것을 알고 있어야합니다 (Transaction.Current);
테스트 및 증명 나는 SQL Server 데이터베이스에 간단한 테이블을 준비했습니다 :
SELECT * FROM [staging].[TestTable] Column1 ----------- 1
다음과 같이 .NET에서 업데이트 쿼리는 다음과 같습니다
string query = @"UPDATE staging.TestTable SET Column1 = 2";
그리고 command.ExecuteNonQuery () 예외가 발생 직후 :
command.ExecuteNonQuery(); throw new System.InvalidOperationException("Something bad happened.");
여기 참조에 대한 전체 예입니다 :
string query = @"UPDATE staging.TestTable SET Column1 = 2"; using (SqlConnection conn = new SqlConnection(conn2)) { try { conn.Open(); using (TransactionScope ts = new TransactionScope()) { conn.EnlistTransaction(Transaction.Current); using (SqlCommand command = new SqlCommand(query, conn)) { command.ExecuteNonQuery(); throw new System.InvalidOperationException("Something bad happened."); } ts.Complete(); } } catch (Exception) { throw; } }
테스트가 실행되면 그것을 TransactionScope에 완료되기 전에 예외를 발생하고 업데이트 테이블 (트랜잭션 롤백) 불변 값 레스트에 적용되지 않는다. 이 모두가 예상하는대로 의도 된 동작입니다.
Column1 ----------- 1
우리가 conn.EnlistTransaction (Transaction.Current)와 트랜잭션에서 연결을 입대 잊어 버린 경우 이제 어떻게;?
예를 재실행하면 다시 예외를 유발하여 실행 흐름 캐치 블록으로 바로 점프한다. ts.Complete ()이기는하지만; 테이블 값이 변경 호출되지 않습니다 :
Column1 ----------- 2
트랜잭션 범위가도록 SqlConnection 후에 선언으로 연결이 범위를 인식하지 않고 암시 적으로 소위 주변 트랜잭션에 참여하지 않습니다.
데이터베이스 바보에 대한 더 깊은 분석
실행이 command.ExecuteNonQuery () 후 일시 정지하는 경우에도 깊이 파고하려면; 예외가 발생하기 전에 우리는 다음과 같이 데이터베이스 (SQL 서버)에 거래를 조회 할 수 있습니다 :
SELECT tst.session_id, tat.transaction_id, is_local, open_transaction_count, transaction_begin_time, dtc_state, dtc_status FROM sys.dm_tran_session_transactions tst LEFT JOIN sys.dm_tran_active_transactions tat ON tst.transaction_id = tat.transaction_id WHERE tst.session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE program_name = 'TransactionScopeTest')
이 연결 문자열에 응용 프로그램 이름 속성을 통해 세션 program_name은을 설정할 수 있습니다를 수행 응용 프로그램 이름 = TransactionScopeTest;
현재 기존의 트랜잭션은 아래 펼쳐지고 :
session_id transaction_id is_local open_transaction_count transaction_begin_time dtc_state dtc_status ----------- -------------------- -------- ---------------------- ----------------------- ----------- ----------- 113 6321722 1 1 2018-11-30 09:09:06.013 0 0
conn.EnlistTransaction (Transaction.Current)없이; 어떤 트랜잭션이 활성 연결에 바인딩되지 않습니다 따라서 변경 트랜잭션 컨텍스트에서 발생하지 않습니다
session_id transaction_id is_local open_transaction_count transaction_begin_time dtc_state dtc_status ----------- -------------------- -------- ---------------------- ----------------------- ----------- -----------
.NET Framework는 .NET 핵심 대 비고 .NET 코어 I 내 테스트하는 동안 다음과 같은 예외를 건너 왔어요 :
System.NotSupportedException: 'Enlisting in Ambient transactions is not supported.'
현재 범위가 전이나도록 SqlConnection 후 초기화 여부에 상관없이 TransactionScope의 접근 방식을 지원하지 않는 .NET 코어 (2.1.0)을 보인다.
from https://stackoverflow.com/questions/2912112/cannot-access-sqltransaction-object-to-rollback-in-catch-block by cc-by-sa and MIT license
'SQL' 카테고리의 다른 글
[SQL] 어떻게 엔티티 프레임 워크는 문자 (N) 필드에 매핑 특정 컬럼에 대한 검색 자동 트림 값으로 구성 할 수 있습니다? (0) | 2020.06.03 |
---|---|
[SQL] PostgreSQL를 9.3로 가져 오기 엑셀 데이터 (0) | 2020.06.03 |
[SQL] 데이터베이스 좋은지 나쁜지에 VARCHAR 외부 키로 / 기본 키? (0) | 2020.06.03 |
[SQL] 나는 CASCADE DELETE 규칙을 사용해야합니까? [복제] (0) | 2020.06.03 |
[SQL] 어떻게 PostgreSQL 데이터베이스 테이블의 컬럼의 위치를 변경합니까? (0) | 2020.06.03 |