在更新另一个 table 时,EF4 RowCount 问题而不是插入触发器

EF4 RowCount issue on instead of insert trigger while updating an other table

我在使用 entityFramework 4 时遇到了一些问题。事情是这样的: 我们有一个 SQL 服务器数据库。每个 table 有 3 个代替触发器用于插入、更新和删除。 我们知道 EntityFramework 有一些问题来处理这些触发器,这就是为什么我们在触发器末尾添加以下代码以强制 rowCount :

用于插入:

DECLARE @Identifier BIGINT;
SET @Identifier = scope_identity()
SELECT @Identifier AS Identifier

对于 update/delete :

CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable

到目前为止一切正常: 从一个而不是插入触发器(假设 table A),我尝试更新另一个 table (table B)

的字段

我知道我的更新代码可以完美运行,因为手动插入可以完成工作。仅当我使用 Entity framework.

时才会出现此问题

我现在有了解决方案,让我们用一个完整的例子来做一个学校案例。 :)

在这个例子中,我们的应用程序是一个地址簿。我们要更新业务 Activity(业务中的 IsActive 列) 每次我们添加、更新或删除此业务的联系人时。如果至少有一位联系人,则该业务被视为活跃 的业务是活跃的。我们在 table 中记录业务的每个状态变化,以获得完整的历史记录。

所以,我们有 3 tables :

table 业务(标识符(PK 身份)、名称、IsActive), table 联系人(标识符(PK 身份)、姓名、IsActive、IdentifierBusiness) table BusinessHistory(标识符(PK 身份)、IsActive、日期、IdentifierBusiness)

这是我们感兴趣的触发器:

table 联系人(触发 IoInsert):

-- inserting the new rows
INSERT INTO Contact
(
     Name
    ,IsActive
    ,IdentifierBusiness
)
SELECT
    t0.Name
    ,t0.IsActive
    ,t0.IdentifierBusiness
FROM
    inserted AS t0
-- Updating the business

UPDATE
    Business
SET
    IsActive = CASE WHEN 
            (
                (t0.IsActive = 1 AND Business.IsActive = 1)
                OR
                (t0.IsActive = 1 AND Business.IsActive = 0)
            ) THEN 1 ELSE 0
FROM
    inserted AS t0
WHERE
    Business.Identifier = t0.IdentifierBusiness
AND
    t0.IsActive = 1
AND
    Business.IsActive = 0   



-- Forcing rowCount for EntityFramework 
DECLARE @Identifier BIGINT;
SET @Identifier = scope_identity()
SELECT @Identifier AS Identifier

Table 业务(触发 IoUpdate)

UPDATE
    Business
SET
    IsActive = 1
FROM
    Contact AS t0
WHERE
    Business.Identifier = t0.IdentifierBusiness
AND
    t0.IsActive = 1
AND
    Business.IsActive = 0   


---- Updating BusinessHistory

INSERT INTO BusinessHistory
(
    Date
    ,IsActive
    ,IdentifierBusiness
)
SELECT
    DATE()
    ,t0.IsActive
    ,t0.Identifier
FROM
    inserted AS t0
INNER JOIN
    deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE   
    (t0.Identifier <> t1.Identifier)


-- Forcing rowCount for EntityFramework 
CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable   

Table 业务历史:

-- Updating the business

UPDATE
    Business
SET
    IsActive = CASE WHEN 
            (
                (t0.IsActive = 1 AND Business.IsActive = 1)
                OR
                (t0.IsActive = 1 AND Business.IsActive = 0)
            ) THEN 1 ELSE 0
FROM
    inserted AS t0
WHERE
    Business.Identifier = t0.IdentifierBusiness
AND
    t0.IsActive = 1
AND
    Business.IsActive = 0   

-- inserting the new rows
INSERT INTO BusinessHistory
(
    Date
    ,IsActive
    ,IdentifierBusiness
)
SELECT
    DATE()
    ,t0.IsActive
    ,t0.Identifier
FROM
    inserted AS t0

-- Forcing rowCount for EntityFramework 
DECLARE @Identifier BIGINT;
SET @Identifier = scope_identity()
SELECT @Identifier AS Identifier

所以,简而言之,发生了什么?

我们有 2 个 table,业务和联系人。联系人正在更新 table 插入和更新业务。

当 Business 更新时,它会插入 BusinessHistory,它存储 table Business 的更新历史 ,当字段 IsActive 更新时。

问题是,即使我不在 BusinessHistory 中插入新行,我也会启动插入指令,因此,我进入了 table BusinessHistory 的 instead of insert 触发器。当然,在这一个的最后,还有一个scope_identity()。您只能使用 scope_identity 一次,它返回最后插入的标识。 因此,由于我没有插入任何 BusinessHistory,它正在使用我新插入的联系人的 scope_identity : scope_identity 而不是 联系人插入 table 为空!

如何隔离问题?

  • 使用探查器,您发现在 BusinessHistory 中有插入指令,但它不应该是任何插入指令。

  • 使用调试,你最终会在你不应该在的插入触发器中结束。

如何解决?

这里有几种选择。我所做的是通过 If 条件在 table Business 中插入 BusinessHistory : 我希望仅当状态 "IsActive" 发生更改时才插入插入内容:

IF EXISTS
(
    SELECT
        1
    FROM
        inserted AS t0
    INNER JOIN
        deleted AS t1 ON t0.Identifier = t1.Identifier
    WHERE
        (t0.IsActive <> t1.IsActive)
)
BEGIN
    INSERT INTO BusinessHistory
    (
        Date
        ,IsActive
        ,IdentifierBusiness
    )
    SELECT
        DATE()
        ,t0.IsActive
        ,t0.Identifier
    FROM
        inserted AS t0
    INNER JOIN
        deleted AS t1 ON t0.Identifier = t1.Identifier
    WHERE   
        (t0.IsActive <> t1.IsActive)
END

另一种可能性是,在触发器中而不是插入 table BusinessHistory,用 IF EXISTS 条件包围整个触发器

IF EXISTS (SELECT 1 FROM inserted)
BEGIN
    ----Trigger's code here ! 
END

如何避免?

  • 好吧,使用这些修复程序之一!
  • 避免scope_identity(),在大多数情况下@@IDENTITY 就足够了!在我的公司,我们只使用 scope_identity 因为 EF 4 !

我知道我的英语不完美,如果它不够好,或者如果有人想在这个主题上添加一些东西,我可以编辑!