应用程序有时因触发器的结果集而崩溃

时间:2017-12-05 17:59:22

标签: sql-server entity-framework azure asp.net-mvc-4 triggers

我们公司对每个相关数据库表执行基于触发器的审计日志记录,以便在每次更改数据时跟踪应用程序用户的更改。 这背后的概念很简单。比方说,我们有一个我们想要历史记录的简单表格,如下所示:

CREATE TABLE Product
(
    ProductId INT Identity(1,1) NOT NULL,
    Name VARCHAR(32) NOT NULL,
    CONSTRAINT pk__product PRIMARY KEY (ProductId)
);

首先,我们创建第二个表,其中包含将应用于上表的每个写操作的完整历史记录:

CREATE TABLE Product__Hist
(
    Revision INT NOT NULL,--Starts at 1. Will be incremented on every update.
    IsRecordDeleted BIT NOT NULL,--Indicates whether the record still exists or has been deleted
    [User] VARCHAR(8) NOT NULL,--Identifies the user that did the change
    [Timestamp] DATETIME NOT NULL,--Timestamp of the user-activity
    ProductId INT NOT NULL,
    Name VARCHAR(32) NOT NULL,
    CONSTRAINT pk__product__hist PRIMARY KEY CLUSTERED (ProductId ASC, Revision DESC)
);

最后但并非最不重要的是,我们需要«Product»表上的触发器,它将在需要时将数据写入历史表。触发器将在任何类型的数据修改上调用,例如插入,更新和删除:

CREATE TRIGGER tr__product__a__iud ON Product
AFTER INSERT, UPDATE, DELETE
AS
DECLARE
    @productid INT,
    @revision INT,
    @user VARCHAR(8),
    @name VARCHAR (32)
DECLARE
    @usercontext TABLE (Usr VARCHAR(8))
BEGIN
    INSERT INTO @usercontext (Usr)
    EXEC dbo.GetContextInfo
    SELECT @user = Usr FROM @usercontext--Retrieves user's identity

    IF EXISTS(SELECT * FROM INSERTED)--INSERT OR UPDATE
    BEGIN
        DECLARE crs CURSOR FOR
            SELECT ProductId, Name  FROM inserted;
        OPEN crs
        FETCH NEXT FROM crs INTO @productid, @name
        WHILE @@FETCH_STATUS = 0
            BEGIN
                SELECT @revision = COALESCE((SELECT Max(Revision) FROM Product__Hist WHERE ProductId = @productid),0)+1
                INSERT INTO Product__Hist(ProductId, Revision, IsRecordDeleted, [User],  Name) VALUES (@productid, @revision, 0, @user, @name)

                FETCH NEXT FROM crs INTO @productid, @name
            END
        CLOSE crs
        DEALLOCATE crs
    END
    ELSE IF EXISTS(SELECT * FROM DELETED)--DELETE
    BEGIN
        DECLARE crs CURSOR FOR
            SELECT ProductId, Name  FROM deleted;
        OPEN crs
        FETCH NEXT FROM crs INTO @productid, @name
        WHILE @@FETCH_STATUS = 0
            BEGIN
                SELECT @revision = COALESCE((SELECT Max(Revision) FROM Product__Hist WHERE ProductId = @productid),0)+1
                INSERT INTO Product__Hist(ProductId, Revision, IsRecordDeleted, [User],  Name) VALUES (@productid, @revision, 1, @user, @name)

                FETCH NEXT FROM crs INTO @productid, @name
            END
        CLOSE crs
        DEALLOCATE crs
    END
END

现在出现问题:大多数情况下,这个解决方案完全正常。但是,我不得不体验到,当触发器被调用时,实体框架有时会出现以下异常:«触发器返回结果集和/或运行时SET NOCOUNT OFF而另一个未完成的结果集处于活动状态»。可悲的是,我无法找出这种行为的根本原因。 以下是一些可能有用的额外输入以及我迄今为止尝试过的内容:

  • 我尝试在触发器开始时设置NOCOUNT ON,并在触发器结束时设置NOCOUNT OFF(根据:A trigger returned a resultset and/or was running with SET NOCOUNT OFF while another outstanding result set was active);没有帮助
  • 我尝试重现问题(隔离导致问题的数据记录),但失败了。
  • 肯定要对触发器做一些事情,因为如果我禁用它就不会抛出异常
  • 我发现问题与我们的触发器需要编写的数据量无关
  • 我尝试将“禁止触发器的结果”设置为1(根据Add SET NOCOUNT ON on top of triggers)。此解决方案在我的本地工作站上运行良好(DB:SQL-Server 2014)。但是,如果数据库托管在Microsoft Azure上,则会出现以下错误(40510):
      

    此版本的SQL Server不支持语句'CONFIG'。

我们非常感谢任何可以帮助我解决这个奇怪问题的信息。提前谢谢!

我知道关于同一个例外的其他问题很少。但是,由于我没有找到任何对我有帮助的东西,所以我决定去寻找一个新问题。

0 个答案:

没有答案