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

Pau*_*man 8 triggers entity-framework sql-server-2008-r2 .net-4.0-beta-2

使用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
Run Code Online (Sandbox Code Playgroud)

在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.
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢! - 保罗

Rya*_*oss 8

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

我在这个StackOverflow线程中找到了一个答案,建议在触发器的末尾添加以下行(如果项目不匹配且执行了Insert).

select [Id] from [dbo].[TableXXX] where @@ROWCOUNT > 0 and [Id] = scope_identity() 
Run Code Online (Sandbox Code Playgroud)

我用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
Run Code Online (Sandbox Code Playgroud)