C # 코드 및 저장 프로 시저에서 트랜잭션을 처리해야합니까?


14

C #뿐만 아니라 데이터베이스 저장소 프로세스 양쪽에서 트랜잭션 처리가 실제로 필요합니까?

씨#:

Using(transaction with transaction scope)
{
     Execute stored proc;
     Transaction. Complete;
}

SQL 저장 프로 시저 :

Create process
As
Begin try
    Begin transaction
    Commit
End try
Begin catch
    Rollback
End catch

답변:


20

먼저 , 모든 프로 시저에서 항상 적절한 트랜잭션 처리를해야 앱 코드, 다른 프로 시저, 개별 쿼리, 개별 쿼리, SQL 에이전트 작업 또는 기타 다른 방법으로 호출되는지 여부는 중요하지 않습니다. . 그러나 단일 DML 문 또는 수정하지 않는 코드 에는 명시 적 트랜잭션이 필요 하지 않습니다 . 그래서, 내가 추천하는 것은 :

  • 오류가 올바르게 버블 링 될 수 있도록 항상 TRY / CATCH 구조를 갖습니다.
  • DML 문이 여러 개인 경우 단일 코드 자체가 트랜잭션이므로 아래 코드에 3 개의 트랜잭션 처리 부분을 선택적으로 포함하십시오. 그러나, 특별히 필요하지 않은 코드를 추가하는 것 외에, 일관된 템플릿을 선호하는 경우 3 개의 트랜잭션 관련 IF 블록을 유지하는 것이 아프지 않습니다. 그러나이 경우에도 SELECT 전용 (즉 읽기 전용) 프로세스에 대해 3 개의 트랜잭션 관련 IF 블록을 유지 하지 않는 것이 좋습니다 .

둘 이상의 DML 문을 수행 할 때 필요 (하나의 일관성을 선호하는 경우도 하나의 DML 작업에 수행 할 수 있습니다) 다음의 라인을 따라 뭔가를 사용 :

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT;

BEGIN TRY

    IF (@@TRANCOUNT = 0)
    BEGIN
        SET @InNestedTransaction = 0;
        BEGIN TRAN; -- only start a transaction if not already in one
    END;
    ELSE
    BEGIN
        SET @InNestedTransaction = 1;
    END;

    -- { 2 or more DML statements (i.e. INSERT / UPDATE / DELETE) }

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        COMMIT;
    END;

END TRY
BEGIN CATCH

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        ROLLBACK;
    END;

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

단 하나의 DML 문이나 SELECT를 수행 할 때 다음과 같은 방법으로 벗어날 수 있습니다.

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;

BEGIN TRY

    -- { 0 or 1 DML statements (i.e. INSERT / UPDATE / DELETE) }

END TRY
BEGIN CATCH

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

둘째 , 둘 이상의 쿼리 / 저장 프로 시저를 실행해야하고 모두 원자 단위로 그룹화해야하는 경우 에만 앱 계층에서 트랜잭션을 처리 해야합니다. 싱글 SqlCommand.Execute___을하는 것은 시도 / 캐치에만 있어야하지만 거래에는 없습니다.

그러나 한 번만 전화를 걸 때 앱 계층에서 트랜잭션을 수행하는 것이 좋지 않습니까? MSDTC (Microsoft Distributed Transaction Coordinator)가 필요한 경우 명시 적으로 필요하지 않을 때 앱 계층 에서이 작업을 수행하는 것이 시스템에서 약간 더 무겁습니다. 개인적으로, 고아 트랜잭션의 가능성을 줄임으로써 커밋 또는 롤백 전에 앱 코드에 문제가있는 경우 앱 레이어 기반 트랜잭션을 피하는 것이 좋습니다. 또한 때로는 특정 상황을 디버깅하는 것이 조금 더 어려워진다는 것을 알았습니다. 그러나 그것은 기술적으로 것이 보이지 않습니다 문제 하나 만들 때 응용 계층에서 트랜잭션을 처리 시저를요구; 다시 말하지만, 단일 DML 문은 자체 트랜잭션이므로 두 계층에서 명시적인 트랜잭션 처리 가 필요 하지 않습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.