В моих хранимых процессах я использую Try-Catch и вызываю процедуру обработки ошибок в блоке Catch, которая записывает подробности об ошибках в таблицу ErrorLog, а затем перебрасывает ошибку.
В своем коде C # я выполняю свои хранимые процедуры, используя:
using(TransactionScope scope = new TransactionScope()) {
// execute stored procs
scope.Complete();
}
Проблема, с которой я сталкиваюсь, если транзакция прервана (scope.Complete никогда не вызывается), мой обработанный обработчик ошибок proc действительно выдает ошибку 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