使用实体框架进行插入表操作时出现触发器错误

29
我正在使用Entity Framework 4,在使用Entity Framework插入一条新记录到一个包含触发器的表时,该表有一个自增列。如果表有一个Instead of Insert触发器,用于根据某些逻辑修改插入的值,则Entity Framework会引发异常“存储更新、插入或删除语句影响了意外数量的行数(0)。实体可能已经被修改或删除自从实体被加载以来,请刷新ObjectStateManager条目”。有人能帮忙解决这个异常吗?

1
你为什么没有标记一个正确的答案呢?Ryan Gross在下面的回答中为我解决了完全相同的问题。 - Tyler Jones
4个回答

60

使用Entity Framework 4.1,Ladislav发布的解决方案在触发器的末尾添加了Select Scope_Identity()语句,这对我解决了问题。为了完整起见,我在此处复制了整个触发器创建过程。使用这个触发器定义,我能够使用context.SaveChanges()向表中添加行。

ALTER TRIGGER [dbo].[CalcGeoLoc]
   ON  [dbo].[Address]
   INSTEAD OF INSERT
AS 
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT OFF;

-- Insert statements for trigger here
INSERT INTO Address (Street, Street2, City, StateProvince, PostalCode, Latitude, Longitude, GeoLoc, Name)
SELECT Street, Street2, City, StateProvince, PostalCode, Latitude, Longitude, geography::Point(Latitude, Longitude, 4326), Name 
FROM Inserted;

select AddressId from [dbo].Address where @@ROWCOUNT > 0 and AddressId = scope_identity();
END

修改以处理计算值(感谢评论中的Chris Morgan):

如果表格中还有其他的计算值,你也需要在SELECT语句中将它们包括在内。例如,如果你有一个使用GETDATE()函数的CreatedDate列,你应该这样写SELECT语句:

SELECT [AddressId], [CreatedDate] from [dbo].Addresses where @@ROWCOUNT > 0 and AddressId = scope_identity();

3
我在触发器的结尾添加了 "SELECT <columnName> FROM INSERTED;",这样就解决了问题。 - muruge
7
你太棒了!如果可以的话,我会投150,000个赞。 - Jeff
4
如果您在表格中有其他计算值,您也需要在SELECT语句中包含它们。例如,如果您有一个使用GETDATE()函数的CreatedDate列,您可以像这样选择:SELECT [AddressId], [CreatedDate] from [dbo].Addresses where @@ROWCOUNT > 0 and AddressId = scope_identity(); - Chris Morgan
1
这个回答解决了我的问题,应该标记为正确答案。 - Tyler Jones
2
非常有用,但需要注意的是,这个解决方案可能在未来版本的SQL Server中无法使用。根据CREATE TRIGGER文档从触发器返回结果的能力将在未来版本的SQL Server中被删除。返回结果集的触发器可能会导致不适用于它们的应用程序出现意外行为。在新开发工作中避免从触发器返回结果集,并计划修改当前执行此操作的应用程序。 - sstan
显示剩余2条评论

15

代替触发器在Entity Framework创建的插入操作之前执行。这可能是一个潜在的问题,因为一旦您使用标识列,每个插入操作都会被以下操作所跟随:

select [Id]
from [dbo].[TableXXX]
where @@ROWCOUNT > 0 and [Id] = scope_identity()
所以问题是,一旦插入被替换后,这个查询会发生什么。如果它被执行并返回null,你会得到一个异常。你可以在触发器中插入记录后添加它,但如果原始查询也被执行,这将不起作用。
您可以将触发器更改为在插入之前或之后,并修改数据。

你能否详细解释一下“但如果原始查询也被执行,则此方法将无济于事。”? - Aducci
在 SQL Server 中,添加信息到此答案之前不存在,而在插入之后比使用代替插入的方式要慢得多。 - Cassio Lemos

2

同时,您还需要返回任何标记为计算属性(Computed)的属性。

select [Id], [YourComputedColumn]
from [dbo].[TableXXX]
where @@ROWCOUNT > 0 and [Id] = scope_identity()

0
我还发现,如果你想在一个nvarchar列上使用自增值作为主键,但该列并不生成自增值,那么你需要将StoreGeneratedPattern设置为Identity才能使其正常工作。这种情况通常出现在插入后触发器计算唯一值以存储在关键列中的情况下。在其他情况下(添加和更新),可能需要将其设置为Computed。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接