【问题标题】:EF4 RowCount issue on instead of insert trigger while updating an other table更新其他表时出现 EF4 RowCount 问题而不是插入触发器
【发布时间】:2017-06-08 13:42:14
【问题描述】:

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

插入:

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

更新/删除:

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

到目前为止它工作得很好: 从而不是插入触发器(比如说表 A),我尝试更新另一个表(表 B)的字段

我知道我的更新代码可以完美运行,因为手动插入可以完成这项工作。仅当我使用实体框架时才会出现此问题。

【问题讨论】:

  • 插入触发器返回哪个scope_identity()?看到这个孤立的代码 sn-p 并不清楚这一点。那时最后插入的记录是什么?
  • 你给了我你的问题的解决方案。在我的表 B 的而不是更新触发器中,我通过插入另一个表来记录表的状态历史记录。我的错误是插入没有被 IF EXISTS 包围,并且第三个表上的插入在错误的时刻消耗了我的 scope_identity()。谢谢你的帮助:)
  • 呵呵,这些触发器总是很棘手。也许你可以把它变成一个答案,这样人们会更容易找到解决方案。

标签: sql-server triggers entity-framework-4 rowcount


【解决方案1】:

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

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

所以,我们有 3 张桌子:

表业务(标识符(PK Identity)、名称、IsActive)、 表联系人(标识符(PK 身份)、姓名、IsActive、IdentifierBusiness) 表BusinessHistory(标识符(PK Identity)、IsActive、Date、IdentifierBusiness)

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

表联系人(触发 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

表业务(触发 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   

表业务历史:

-- 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 张桌子,Business 和 Contact。联系人在插入和更新时更新表业务。

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

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

如何隔离问题?

  • 使用分析器,您会发现 BusinessHistory 中存在插入指令,而它不应该是其中的任何一个。

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

如何解决?

这里有几种选择。我所做的是在表 Business 中通过 If 条件插入 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

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

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

如何避免?

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

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

【讨论】:

    猜你喜欢
    • 2019-09-17
    • 1970-01-01
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多