SQL如何; Try-Catch错误信息到错误日志表,对于在事务中执行的存储过程?

时间:2012-01-09 17:02:03

标签: tsql error-handling translation try-catch

在我的存储过程中,我正在使用Try-Catch,并在Catch块中调用错误处理过程,该过程将在ErrorLog表中记录错误详细信息,然后重新抛出错误。

在我的C#代码中,我使用:

执行我的存储过程
using(TransactionScope scope = new TransactionScope()) {
    // execute stored procs
    scope.Complete();
}

我遇到的问题,如果事务中止(从不调用scope.Complete),我的错误处理存储过程会重新抛出sql错误,但不能将错误记录到ErrorLog表中因为它是在交易的背景下;有没有办法解决 !?我已经知道当事务处于不可提交状态时无法插入数据,那么如何退出事务并仍然记录错误?

TSQL代码:

BEGIN PROCEDURE [dbo].[usp_UpsertSomething]
    @SomethingID BIGINT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        -- do something
    END TRY
    BEGIN CATCH
        EXEC dbo.cp_RethrowError
        RETURN -1
    END CATCH;
END

CREATE PROCEDURE [dbo].[usp_RethrowError]
    @ErrorLogID [INT] = 0 OUTPUT -- Contains the ErrorLogID of the row inserted
                                 -- by cp_RethrowError in the ErrorLog table.
AS
BEGIN
    -- Return if there is no error information to retrieve.
    IF ERROR_NUMBER() IS NULL
        RETURN;

    DECLARE 
        @ErrorMessage           VARCHAR(4000),
        @FormattedErrorMessage  VARCHAR(4000),
        @ErrorNumber            INT,
        @ErrorSeverity          INT,
        @ErrorState             INT,
        @ErrorLine              INT,
        @ErrorProcedure         VARCHAR(200);

    -- Assign variables to error-handling functions that 
    -- capture information for RAISERROR.
    SELECT 
        @ErrorNumber = ERROR_NUMBER(),
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE(),
        @ErrorLine = ERROR_LINE(),
        @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

    -- Build the message string that will contain original
    -- error information.
    SELECT @FormattedErrorMessage = 
        'ErrorLogID %d, Error %d, Level %d, State %d, Procedure %s, ' + 
            'Line %d, Message: '+ @ErrorMessage;

    BEGIN TRY
        -- Data insertion/modification is not allowed when 
        -- a transaction is in an uncommittable state.
        IF XACT_STATE() = -1 BEGIN
            SET @ErrorLogID = 0;
        END
        ELSE BEGIN
            INSERT [dbo].[ErrorLog] 
            (
                ErrorNumber, 
                ErrorSeverity, 
                ErrorState, 
                ErrorProcedure, 
                ErrorLine, 
                ErrorMessage
            ) 
            VALUES 
            (
                @ErrorNumber,
                @ErrorSeverity,
                @ErrorState,
                @ErrorProcedure,
                @ErrorLine,
                @ErrorMessage
            );

            -- Pass back the ErrorLogID of the row inserted
            SELECT @ErrorLogID = @@IDENTITY;
        END
    END TRY
    BEGIN CATCH
        PRINT 'An error occurred in stored procedure cp_RethrowError.';
    END CATCH

    -- Raise an error: msg_str parameter of RAISERROR will contain
    -- the original error information.
    RAISERROR 
    (
        @FormattedErrorMessage, 
        @ErrorSeverity, 
        1,
        @ErrorLogID,     -- parameter: ErrorLogID in ErrorLog table.
        @ErrorNumber,    -- parameter: original error number.
        @ErrorSeverity,  -- parameter: original error severity.
        @ErrorState,     -- parameter: original error state.
        @ErrorProcedure, -- parameter: original error procedure name.
        @ErrorLine       -- parameter: original error line number.
    );
END

3 个答案:

答案 0 :(得分:0)

由于事务是一个全有或全无的交易,完成我想做的事情的唯一方法是在调用我的错误处理调用之前调用Catch块内的ROLLBACK TRANSACTION。

我的错误日志以我想要记录的错误结束,但在我的.Net catch块中,我最终得到了来自SQL的投诉:“EXECUTE之后的事务计数表明缺少COMMIT或ROLLBACK TRANSACTION语句。之前的计数= 1,当前计数= 0.“

我可以忍受!

答案 1 :(得分:0)

您还可以将错误日志条目放在表变量中(它应该与真实日志表具有相同的结构)。回滚不会清除表变量。最后,您只需将表变量的内容插入到真实的日志表中。

我没试过,但它可能会奏效。 SQL Server仍然缺少像自治事务这样的功能(Oracle已经拥有它)。无论您在自主交易中做什么都独立于主要交易。这正是错误记录时所需要的:特别是在回滚事务时,您仍然想知道发生了什么。

答案 2 :(得分:0)

这是一个排序问题。检查错误处理程序中的事务计数。 脚步:   1.首先将原始错误信息输入@variables或@table。   2.检查您的交易计数,如果它大于0,则将其回滚。   3.插入您在步骤1中收集的错误信息。 结束。