1

일부 저장 프로 시저를 체인화하고 있으며 오류 처리가 제대로 작동하는 데 문제가 있습니다. 이러한 저장 프로 시저 중 일부는 오래 실행되므로 저장 프로 시저 내에서 SqlInfoMessageEventHandler 및 코드 (예 : RAISERROR('whatever',10,1) WITH NOWAIT)를 사용하여 작업 진행률을 사용자에게보고했습니다. 이를 위해 cmd.ExecuteNonQuery()를 사용할 수없고 대신 cmd.ExecuteReader()를 사용해야한다는 내용을 읽었습니다. 나는 이것을 시도해 보았다. ExecuteNonQuery에서 메시지가 표시되지 않습니다.ExecuteReader를 사용하여 SQL Server 체인 된 저장된 proc을 ErrorHandling

내가 발견 한 문제는 ExecuteReader를 사용할 때 저장된 proc이 오류를 throw하면 무시됩니다. 이것은 내 .NET 응용 프로그램을 try 블록에서 호출하고 저장 프로 시저에 SELECT 1/0과 같은 오류가 발생하면 실행이 catch 블록에 입력되지 않고 내 트랜잭션을 커밋한다는 것을 의미합니다. 예를 들면 다음과 같습니다.

일부 .NET 코드 등으로
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TempTstTbl]') AND type in (N'U')) 
DROP TABLE [dbo].[TempTstTbl] 

CREATE TABLE [dbo].[TempTstTbl] (
    Step INT, 
    Val VARCHAR(50) 
) 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Child]') AND type in (N'P', N'PC')) 

DROP PROCEDURE [dbo].[Child] 
GO 
CREATE PROCEDURE [dbo].[Child] 
AS 
BEGIN 
BEGIN TRY 
    INSERT INTO [dbo].[TempTstTbl] (Step, Val) VALUES (1, 'FROM CHILD BEFORE FAULT') 
    SELECT 1/0 
    --RAISERROR ('This should really fail', 16, 2) 
    INSERT INTO [dbo].[TempTstTbl] (Step, Val) VALUES (2, 'FROM CHILD AFTER FAULT') 
END TRY 
BEGIN CATCH 
    DECLARE @ErrorMessage NVARCHAR(4000); 
    DECLARE @ErrorSeverity INT; 
    DECLARE @ErrorState INT; 

    SELECT 
     @ErrorMessage = ERROR_MESSAGE(), 
     @ErrorSeverity = ERROR_SEVERITY(), 
     @ErrorState = ERROR_STATE(); 

    -- Use RAISERROR inside the CATCH block to return error 
    -- information about the original error that caused 
    -- execution to jump to the CATCH block. 
    RAISERROR (@ErrorMessage, -- Message text. 
       @ErrorSeverity, -- Severity. 
       @ErrorState -- State. 
       ); 
END CATCH; 
END 
GO 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Parent]') AND type in (N'P', N'PC')) 

DROP PROCEDURE [dbo].[Parent] 
GO 
CREATE PROCEDURE [dbo].[Parent] 
AS 
BEGIN 
BEGIN TRY 
    INSERT INTO [dbo].[TempTstTbl] (Step, Val) VALUES (1, 'FROM PARENT BEFORE CHILD') 
    Exec [dbo].[Child] 
    INSERT INTO [dbo].[TempTstTbl] (Step, Val) VALUES (2, 'FROM PARENT AFTER CHILD') 
END TRY 
BEGIN CATCH 
    DECLARE @ErrorMessage NVARCHAR(4000); 
    DECLARE @ErrorSeverity INT; 
    DECLARE @ErrorState INT; 

    SELECT 
     @ErrorMessage = ERROR_MESSAGE(), 
     @ErrorSeverity = ERROR_SEVERITY(), 
     @ErrorState = ERROR_STATE(); 

    -- Use RAISERROR inside the CATCH block to return error 
    -- information about the original error that caused 
    -- execution to jump to the CATCH block. 
    RAISERROR (@ErrorMessage, -- Message text. 
       @ErrorSeverity, -- Severity. 
       @ErrorState -- State. 
       ); 
END CATCH; 
END 
GO 

EXEC [dbo].[Parent] 
SELECT * FROM [dbo].[TempTstTbl] 

;

private void button4_Click(object sender, EventArgs e) 
{ 
    using (SqlConnection conn = new SqlConnection(@"Data Source=XPDEVVM\XPDEV;Initial Catalog=MyTest;Integrated Security=SSPI;")) 
    { 
     conn.Open(); 
     using (SqlTransaction trans = conn.BeginTransaction()) 
     { 
      using (SqlCommand cmd = conn.CreateCommand()) 
      { 
       cmd.Transaction = trans; 
       cmd.CommandText = "[cfg].[Parent]"; 
       cmd.CommandType = CommandType.StoredProcedure; 

       try 
       { 
        -- cmd.ExecuteReader(); -- Using this instead of ExecuteNonQuery means the divide by 0 error in the stored proc is ignored, and everything is committed :(
        cmd.ExecuteNonQuery(); 
        trans.Commit(); 
       } 
       catch (Exception ex) 
       { 
        trans.Rollback(); 
       } 
      } 
     } 
    } 
} 

사람이 내 저장된 프로 시저에서 내 진행 메시지를 얻을 수있는 방법에 대한 아이디어를 가지고 있지만, 여전히 오류가 저장 프로 시저에서 발생하는 경우 .NET 예외를 잡을 수 있습니까?

답변

1

이 문제는 DataReader의 작동 방식에서 비롯된 것으로 보입니다. 나는 먼저 this answer에있는 Nelson Rothermel 코멘트에 대한 힌트를 찾아왔다.

다음은이 문제에 대한 자세한 내용을 this thread에 게시했습니다. Richard McKinnon은이 문제를 해결할 수있는 방법으로 다음 예제를 제공합니다 (필자의 강조).

(나는에서는 Northwind DB를 사용하여 확인) (I 11를 사용하고 있습니다) 심각도> (10)와 같은 시도하고 SP에 SELECT 줄을 추가합니다. 을 보면 데이터를 검색하는 SELECT가있을 때 예외가 발생하지 않습니다.

프로세스를 결과, 선택 문장의 제 1 세트를 시작한다,이 경우가 ExecuteReader()에서

를 [원본 메시지 클립핑]. 다음 결과 집합을 얻으려면 RAISERROR, 에 NextResult()을 호출해야합니다.

그래서 오류를 살펴 보려면 코드를 이전에서 으로 변경해야합니다.

SqlDataReader dr = command.ExecuteReader(); dr.NextResult(); dr.Close();

내 코드에 dr.NextResult()를 추가하려고 시도했지만 문제가 해결 된 것 같습니다.

이렇게하면 저장된 proc에서 정보 메시지를 얻을 수 있지만 저장된 proc에서도 발생하는 오류를 잡을 수 있습니다.

관련 문제