EF4 行计数问题,而不是在更新其他表时插入触发器



我在使用实体框架 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)

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

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

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

所以,我们有 3 张表:

表业务(标识符(PK 标识)、名称、活动), 表 联系人(标识符(PK 标识)、姓名、活动身份、标识符业务) 表业务历史记录(标识符(PK 标识)、活动、日期、标识符业务)

以下是我们感兴趣的触发器:

表联系人(触发 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张桌子,商务和联系方式。联系人正在更新表业务插入和更新。

当业务更新时,它会插入到业务历史记录中,该历史记录存储表业务的更新历史记录 ,当字段处于活动状态更新时。

问题是,即使我不在业务历史记录中插入新行,我也会启动一个插入指令,因此,我进入而不是插入表业务历史记录的触发器。当然,在这篇文章的最后,有一个scope_identity()。您只能使用一次scope_identity,它会返回插入的最后一个标识。 因此,由于我没有插入任何业务历史记录,因此它消耗了我新插入的联系人的scope_identity scope_identity:而不是 联系人表的插入是空的!

如何隔离问题?

  • 使用探查器,您可以发现业务历史记录中有插入指令,而它不应该是其中任何一个。

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

如何解决?

这里有几种选择。我所做的是在表中用 If 条件将业务历史的插入包围起来: 我希望仅在状态"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

另一种可能性是,在触发器中而不是插入表业务历史记录,用 IF EXISTS 条件包围整个触发器

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

如何避免呢?

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

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

最新更新