9

使用 SQL 2008 R2 11 月版本数据库和 .net 4.0 Beta 2 Azure 辅助角色应用程序。辅助角色收集数据并将其插入到具有一个标识列的单个 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

在工作人员角色中,我将实体框架用于对象模型。当我调用 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?

谢谢!-保罗

4

2 回答 2

9

正如您所怀疑的那样,问题在于任何插入到带有 Identity 列的表后,都会立即选择 scope_identity() 以填充实体框架中的关联值。而不是触发器会导致错过第二步,从而导致插入 0 行错误。

我在这个 StackOverflow 线程中找到了一个答案,建议在触发器末尾添加以下行(在项目不匹配并且执行插入的情况下)。

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

我用 Entity Framework 4.1 对此进行了测试,它为我解决了这个问题。为了完整起见,我在这里复制了我的整个触发器创建。使用此触发器定义,我能够通过将地址实体添加到上下文并使用 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
于 2011-06-10T04:19:23.993 回答
3

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

SELECT SCOPE_IDENTITY() AS CentrePersonID;

在我的触发器结束时CentrePersonID,具有自动递增标识的基础表的键字段的名称在哪里。这样 EF 就可以发现新插入的记录的 ID。

于 2013-12-12T17:17:56.750 回答