7

使用SQL 2008 R2 11月發行版數據庫和.net 4.0 Beta 2 Azure工作程序角色應用程序。 worker角色收集數據並將其插入到具有一個標識列的單個SQL表中。因爲這個worker角色可能會有多個實例在運行,所以我在SQL表上創建了一個Insert Between Of觸發器。觸發器使用SQL合併功能執行Upsert功能。使用T-SQL,我能夠正確驗證插入而不是觸發器函數,在更新現有行時插入新行。 這是我的觸發代碼:我使用實體框架的對象模型OptimisticConcurrencyException - SQL 2008 R2,而不是使用實體框架插入觸發器

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方法我receieve以下異常:

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. 

我明白這是likly由於SQL不回報告爲每個新的插入/更新的行的IdentityScope已。然後EF認爲行沒有被插入,並且事務最終沒有被提交。

處理此異常的最佳方法是什麼?也許使用SQL合併函數中的OUTPUT?

謝謝! -Paul

回答

7

如您所懷疑的,問題是任何插入帶有標識列的表中的緊隨其後的是選擇scope_identity()以填充實體框架中的關聯值。而不是觸發器會導致第二步被錯過,導致0行插入錯誤。

我在this StackOverflow thread找到了一個答案,建議在觸發器的末尾添加以下行(如果該項不匹配並且執行插入)。

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

我用Entity Framework 4.1測試過,它解決了我的問題。爲了完整起見,我在這裏複製了我的整個觸發器。通過這個觸發器defenition,我可以通過將地址實體添加到上下文並使用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

如果您正在進行INSTEAD OF UPDATE,只要確保在INSERT語句之前「SET NOCOUNT OFF」,並且不要在它之後進行任何類型的選擇。 – 2012-04-25 17:57:02

+0

應該標記爲答案 – 2013-03-27 22:53:59

3

我幾乎一模一樣的場景:實體框架驅動插入到一個視圖與INSTEAD OF INSERT觸發它是導致「......行的意外數(0)...」異常。由於瑞安格羅斯的答案,我通過我的觸發,其中CentrePersonID是具有自動inrcementing身份的基礎表的鍵字段名的末尾添加

SELECT SCOPE_IDENTITY() AS CentrePersonID; 

固定它。這樣EF可以發現新插入記錄的ID。

+0

我剛剛發現,即使字段名稱不區分大小寫的DB端,上面編碼的字段名稱區分大小寫,並且必須完全匹配等效的Key屬性代碼端。 – 2014-01-02 12:54:32