在更新另一个 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 !
我知道我的英语不完美,如果它不够好,或者如果有人想在这个主题上添加一些东西,我可以编辑!
我在使用 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 !
我知道我的英语不完美,如果它不够好,或者如果有人想在这个主题上添加一些东西,我可以编辑!