OptimisticConcurrencyException - SQL 2008 R2而不是使用实体框架插入触发器

时间:2009-11-27 04:58:51

标签: entity-framework triggers .net-4.0-beta-2 sql-server-2008-r2

使用SQL 2008 R2 11月发行版数据库和.net 4.0 Beta 2 Azure辅助角色应用程序。 worker角色收集数据并将其插入到具有一个标识列的单个SQL表中。因为可能有多个此工作者角色的实例正在运行,所以我在SQL表上创建了一个Insert而不是触发器。触发器使用SQL Merge函数执行Upsert功能。使用T-SQL我能够正确验证插入而不是触发器功能,在更新现有行时插入新行。 这是我的触发器的代码:

Create Trigger [dbo].[trgInsteadOfInsert] on [dbo].[Cars] Instead of Insert
as
begin
set nocount On

merge into Cars as Target
using inserted as Source
on Target.id=Source.id AND target.Manufactureid=source.Manufactureid
when matched then 
update set Target.Model=Source.Model,
Target.NumDoors = Source.NumDoors,
Target.Description = Source.Description,
Target.LastUpdateTime = Source.LastUpdateTime,  
Target.EngineSize = Source.EngineSize
when not matched then
INSERT     ([Manufactureid]
       ,[Model]
       ,[NumDoors]
       ,[Description]
       ,[ID]
       ,[LastUpdateTime]
       ,[EngineSize])
 VALUES
       (Source.Manufactureid,
       Source.Model,
       Source.NumDoors,
       Source.Description,
       Source.ID,
       Source.LastUpdateTime,
       Source.EngineSize);
  End

在worker角色中,我使用Entity Framework作为对象模型。当我调用SaveChanges方法时,我收到以下异常:

OptimisticConcurrencyException
Store update, insert, or delete statement affected an unexpected number of rows (0).    Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

我理解这可能是因为SQL没有为每个新插入/更新的行报告一个IdentityScope。然后EF认为没有插入行,并且最终没有提交事务。

处理此异常的最佳方法是什么?也许使用SQL合并函数中的OUTPUT?

谢谢! -Paul

2 个答案:

答案 0 :(得分:8)

正如您所怀疑的那样,问题是任何对具有Identity列的表的插入都会紧跟一个scope_identity()的选择,以填充Entity Framework中的关联值。而不是触发器导致错过第二步,这导致插入0行错误。

我在this StackOverflow thread中找到了一个答案,建议在触发器的末尾添加以下行(如果项目不匹配且执行了插入)。

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

我使用Entity Framework 4.1进行了测试,它为我解决了这个问题。为了完整起见,我在这里复制了我的整个触发器创建。通过此触发器防御,我可以通过向上下文添加Address实体并使用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

答案 1 :(得分:3)

我几乎完全相同的情况:实体框架驱动的插入到其上有INSTEAD OF INSERT触发器的视图导致“...意外的行数(0)......”异常。感谢Ryan Gross的回答,我通过添加

来修复它
SELECT SCOPE_IDENTITY() AS CentrePersonID;

在我的触发器的末尾,其中CentrePersonID是具有自动引入标识的基础表的键字段的名称。这样EF就可以发现新插入记录的ID。