2009-06-03 4 views
4

트랜잭션에 랩핑 된 두 개의 저장 프로 시저가 있습니다. 여러 가지 이유 때문에 데이터베이스 내에서가 아니라 내 응용 프로그램 코드에서 트랜잭션을 처리해야합니다. 순간 SqlTransaction의 롤백에 대한 예외 처리

, 내 코드는 다음과 같습니다

저장된 발동 중 하나가 오류, 내가 좋아하는 모습을보고하고 예외 메시지 제기
try 
{ 
    using (SqlConnection conn = Connection()) 
    { 
     conn.Open(); 

     using (SqlTransaction sqlTrans = conn.BeginTransaction()) 
     { 
      try 
      { 
       using (SqlCommand cmd1 = new SqlCommand("Stored_Proc_1", conn, sqlTrans)) 
       { 
        cmd1.CommandType = CommandType.StoredProcedure; 
        cmd1.ExecuteNonQuery(); 
       } 

       using (SqlCommand cmd2 = new SqlCommand("Stored_Proc_2", conn, sqlTrans)) 
       { 
        cmd2.CommandType = CommandType.StoredProcedure; 
        cmd2.ExecuteNonQuery(); 
       } 

       sqlTrans.Commit(); 
      } 
      catch 
      { 
        sqlTrans.Rollback(); 

        throw; 
      } 

     } 

     conn.Close(); 
    } 
} 

catch (SqlException ex) 
{ 
    // exception handling and logging code here... 
} 

: 의미가

Error message from raiserror within stored procedure. 
Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0. 

을 첫 번째 catch에서 트랜잭션이 아직 롤백되지 않았기 때문입니다.

하지만 트렌 카운트 메시지가 없는데 " 트랜잭션을 롤백하기 때문에 예외 처리 코드로 인해 관심이 없습니다. 이것을 달성하기 위해 코드를 재구성 할 수있는 방법이 있습니까?

편집 : 내 저장 발동의 기본 구조는 다음과 같습니다

:

create proc Stored_Proc_1 
as 

set nocount on 

begin try 
    begin transaction 

     raiserror('Error raised by Stored_Proc_1', 16, 1)  

    commit 

end try 
begin catch 
    if (@@trancount > 0) rollback 

    declare @ErrMsg nvarchar(4000), @ErrSeverity int, @ErrProc sysname, @ErrLine varchar(10) 
    select @ErrMsg = ERROR_MESSAGE(), @ErrSeverity = ERROR_SEVERITY(), @ErrProc = ERROR_PROCEDURE(), @ErrLine = ERROR_LINE() 

    -- log the error 
    -- sql logging code here... 

    raiserror(@ErrMsg, @ErrSeverity, 1) 
end catch 

UPDATE : 내 저장 프로 시저에서 처리하는 트랜잭션을 수행하고 한 가 보인다 문제를 해결했다. 분명히 나는 ​​그것을 잘못하고 있었다 - 그러나 나는 그것을 올바르게하는 방법을 아직도 알고 싶다. 저장 프로 시저에서 트랜잭션을 제거하는 것이 가장 좋은 솔루션입니까?

답변

5

글쎄, using에 의해 폐쇄 될 것입니다. (생각한 경우, 예외 이후에만 Close()입니다.)

스토어드 프로 시저 중 하나에서 트랜잭션 코드가 자체적으로 수행됩니까 (롤백/커밋되지 않음)? 문제가있는 곳이 인 것 같습니다.은 ...?

-- pseduo-TSQL 
IF @@TRANCOUNT = 0 BEGIN TRAN 
-- ... 
IF @@TRANCOUNT > 0 COMMIT TRAN -- or maybe = 1 

(조건부 할 경우 : 아마도 인해 (잘못된) 방법에 - 어떤 경우, 오류 메시지는 트랜잭션을 시작하지 않은 경우에도 저장 프로 시저 중 하나가 COMMIT을하고 나에게 제안 트랜잭션을 TSQL에서 추적했는지 확인하십시오. 트랜잭션을 생성했는지 여부를 추적해야합니다 (해당되는 경우 COMMIT)

TransactionScope 다른 옵션은 사용하기가 더 쉽습니다 (설정하지 않아도됩니다. 각 명령 등에 대해), 약간 덜 효율적 :

using(TransactionScope tran = new TransactionScope()) { 
    // create command, exec sp1, exec sp2 - without mentioning "tran" or 
    // anything else transaction related 

    tran.Complete(); 
} 

(롤백 등은 없습니다. Dispose() (통해 using) 필요한 경우 롤백을 수행합니다.

+0

의견을 보내 주셔서 감사합니다. 저장된 procs가 잘못 작동했다고 생각하지 않습니다. 기본 구조를 포함하도록 질문을 업데이트했습니다. – kristian

+0

잠깐, 나는 당신이 말하는 것을 보았다고 생각합니다. C# 코드는 트랜잭션이 존재할 것으로 기대하지만, SQL 코드는 그것을 되돌려 놓고 있습니다. – kristian

+1

정확히 ... 라인 "if (@@ trancount> 0) rollback"은 공짜입니다 ... ** 그것이 ** 트랜잭션 ** 생성 여부를 추적하는 플래그를 설정해야합니다. 열린 거래. –

1

Marc는 문제가 스토어드 프로 시저 자체에있을 가능성이 높습니다. 몇 가지 문제를 간략하게 설명하는 매우 흥미로운 기사가 ​​있습니다. here.

4

응용 프로그램에서 이렇게하면 데이터베이스/저장 프로 시저에서 트랜잭션을 수행하지 마십시오!이것은 가장 확실하게 혼란을 일으킬 것입니다. 레이어를 선택하고 붙입니다. 좋은 정규화 된 데이터베이스가 있고 예외가 위쪽으로 퍼트해야합니다.

BEGIN TRY 
    SET @now = CAST(@start AS datetime2(0)) 
END TRY 
BEGIN CATCH 
    SET @now = CURRENT_TIMESTAMP 
END CATCH 

하고, 예를 들어 전달할 : 저장 프로 시저가 같은 코드가 포함되어있는 경우

0

'now'를 @start로 사용하면 시도에서 CAST가 실패합니다. 이는 오류 자체가 캡처되고 처리 되었더라도 트랜잭션을 롤백으로 표시합니다. 따라서 위의 코드에서 예외가 없으면 트랜잭션을 커밋 할 수 없습니다. 저장 프로 시저에 이와 같은 코드가 있으면 try/catch를 피하기 위해 다시 작성해야합니다.