使用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
答案 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。