3

We have implemented Auditing capability using service broker and have implemented triggers on the tables that need to be audited. The issue we are facing is when we try to update an auditable table from within a transaction, it throws up an error -

The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.

However, if we remove the trigger from the auditable table, it all works absolutely fine. is it not possible to have a table (with trigger) be updated within a transaction or are we missing something at our end ?

Update Transaction

BEGIN TRAN
    update  ActivationKey set OrderLineTransactionId = @orderLineTransactionId, LastUpdated = getUtcdate(), [Status] =2  
    where   PurchaseTransactionId = @transactionid 
        -- Rollback the transaction if there were any errors
            IF @@ERROR <> 0 
                ROLLBACK
            ELSE        
                COMMIT TRAN
END TRAN                

Trigger

ALTER TRIGGER [dbo].[ActivationKey_AuditTrigger]
     ON  [dbo].[ActivationKey]
    AFTER INSERT, UPDATE, DELETE 
    AS
    BEGIN
        SET NOCOUNT ON;

        DECLARE @auditBody XML
        Declare @newData nvarchar(MAX)
        DECLARE @DMLType CHAR(1)    
        -- after delete statement
        IF NOT EXISTS (SELECT * FROM inserted)
        BEGIN   
            SELECT  @auditBody = (select * FROM deleted AS t FOR XML AUTO, ELEMENTS),
                    @DMLType = 'D'
        END 
        -- after update or insert statement
        ELSE
        BEGIN
                --after Update Statement
            IF EXISTS (SELECT * FROM deleted)
              begin
                    SELECT      @auditBody = (select * FROM deleted AS t FOR XML AUTO, ELEMENTS)
                    SELECT      @newData = (select * FROM Inserted AS t FOR XML AUTO, ELEMENTS)
                    SELECT      @DMLType = 'U'
              end
              ELSE -- after insert statement
              begin
                    SELECT      @auditBody = (select * FROM inserted AS t FOR XML AUTO, ELEMENTS)
                    SELECT      @DMLType = 'I'
              end
        END

        -- get table name dynamicaly but
        DECLARE @tableName sysname 
        SELECT  @tableName = 'ActivationKey'

        SELECT @auditBody = 
            '<AuditMsg>
                <SourceDb>' + DB_NAME() + '</SourceDb>
                <SourceTable>' + @tableName + '</SourceTable>
                <UserId>' + SUSER_SNAME() + '</UserId>
                <DMLType>' + @DMLType + '</DMLType>
                <ChangedData>' + CAST(@auditBody AS NVARCHAR(MAX)) + '</ChangedData>
                <NewData>' + isnull(@newData,'') + '</NewData>
            </AuditMsg>'
        -- Audit data asynchrounously
        EXEC dbo.procAuditSendData @auditBody
    END 

Stored Proc (procAuditSendData) called from within the trigger

ALTER PROCEDURE [dbo].[procAuditSendData]

( @AuditedData XML ) AS BEGIN BEGIN TRY DECLARE @dlgId UNIQUEIDENTIFIER, @dlgIdExists BIT SELECT @dlgIdExists = 1

    SELECT  @dlgId = DialogId
    FROM    vwAuditDialogs AD 
    WHERE   AD.DbId = DB_ID()
    IF  @dlgId IS NULL
    BEGIN 
        SELECT @dlgIdExists = 0
    END

    -- Begin the dialog, either with existing or new Id
    BEGIN DIALOG @dlgId
        FROM SERVICE    [//Audit/DataSender]                                               
        TO SERVICE      '//Audit/DataWriter', 
                'BAAEA6F1-C97E-4884-8651-2829A2049C46'
        ON CONTRACT     [//Audit/Contract]
    WITH ENCRYPTION = OFF;

    -- add our db's dialog to AuditDialogs table if it doesn't exist yet
    IF @dlgIdExists = 0
    BEGIN 
        INSERT INTO vwAuditDialogs(DbId, DialogId)
        SELECT  DB_ID(), @dlgId
    END
    --SELECT @AuditedData

    -- Send our data to be audited
    ;SEND ON CONVERSATION @dlgId    
    MESSAGE TYPE [//Audit/Message] (@AuditedData)
END TRY
BEGIN CATCH
    INSERT INTO AuditErrors (
            ErrorProcedure, ErrorLine, ErrorNumber, ErrorMessage, 
            ErrorSeverity, ErrorState, AuditedData)
    SELECT  ERROR_PROCEDURE(), ERROR_LINE(), ERROR_NUMBER(), ERROR_MESSAGE(), 
            ERROR_SEVERITY(), ERROR_STATE(), @AuditedData
END CATCH

END

4

2 回答 2

1

我遇到了同样的错误,因为我使用了与您相同的示例:服务代理审计

我终于设法得到了这条消息的错误,这是安全问题。您有单独的数据库用于审计记录。您的 procAuditSendData 在您的 update/insert/delete 命令的上下文中执行(它使用相同的凭据)。就我而言,来自 procAuditSendData 上下文的用户无权访问审计数据库。为了修复您的错误,您必须在单独的审计数据库中添加该上下文用户并授予他数据读取器和数据写入器的权限。我这样做了,之后一切都像魅力一样。

于 2013-02-12T10:17:50.530 回答
1

ERROR_PROCEDURE()在 CATCH 块中发出 a 后,您仍然可以访问etc 函数ROLLBACK TRANSACTION,这是您需要在此处执行的操作。查看在Transact SQL 中使用 TRY...CATCH 中的示例,尤其是查看“错误处理示例”中的代码。它调用的记录错误 ( uspLogError) 的过程在其上方出现了几个示例:

BEGIN CATCH
    -- Call procedure to print error information.
    EXECUTE dbo.uspPrintError;

    -- Roll back any active or uncommittable transactions before
    -- inserting information in the ErrorLog.
    IF XACT_STATE() <> 0
    BEGIN
        ROLLBACK TRANSACTION;
    END

    EXECUTE dbo.uspLogError @ErrorLogID = @ErrorLogID OUTPUT;
END CATCH; 

至于潜在的错误是什么(当前在您的错误报告中出现错误),如果我不得不猜测,您的消息合同将无法处理 XML 中出现的多行数据。但我们需要查看合同以确认这一点。

于 2011-03-08T11:49:46.583 回答