在事务中更新表(使用Service Broker的审计触发器)

时间:2011-03-08 07:59:19

标签: sql sql-server-2008 transactions triggers audit-tables

我们已经使用服务代理实现了审计功能,并在需要审计的表上实现了触发器。我们面临的问题是当我们尝试从事务中更新可审计表时,它会引发错误 -

  

目前的交易不可能   承诺,不能支持   写入日志文件的操作。   回滚交易。

但是,如果我们从可审核表中删除触发器,则一切正常。是不是可以在交易中更新表(带触发器),还是我们在最后遗漏了什么?

更新交易

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                

触发

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 

在触发器内调用存储过程(procAuditSendData)

ALTER PROCEDURE [dbo].[procAuditSendData]

(     @AuditedData XML ) 如 开始     开始尝试         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

2 个答案:

答案 0 :(得分:1)

在您的CATCH块中发布ERROR_PROCEDURE()后,您仍然可以访问ROLLBACK TRANSACTION等功能,这是您需要执行的操作。查看Using TRY...CATCH in Transact SQL中的示例,特别是查看“错误处理示例”中的代码。它调用以记录错误的过程(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中出现的多行数据。但我们需要看到合同来确认这一点。

答案 1 :(得分:1)

我有同样的错误,因为我使用了相同的例子:service broker audit

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