OptimisticConcurrencyException - SQL 2008 R2 вместо триггера вставки с Entity Framework - PullRequest
8 голосов
/ 27 ноября 2009

Использование базы данных выпуска SQL 2008 R2 за ноябрь и приложения .net 4.0 Beta 2 Azure для рабочих ролей. Рабочая роль собирает данные и вставляет их в одну таблицу SQL с одним столбцом идентификаторов. Поскольку, вероятно, будет запущено несколько экземпляров этой рабочей роли, я создал триггер вставки вместо таблицы SQL. Триггер выполняет функцию Upsert с помощью функции SQL Merge. Используя 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

В рамках рабочей роли я использую 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 считает, что строки не были вставлены, и транзакция в конечном итоге не зафиксирована.

Как лучше всего справиться с этим исключением? Может быть, с использованием OUTPUT из функции слияния SQL?

Спасибо! -Поль

Ответы [ 2 ]

8 голосов
/ 10 июня 2011

Как вы и подозревали, проблема в том, что за любыми вставками в таблицу со столбцом Identity сразу же следует выбор scope_identity () для заполнения соответствующего значения в Entity Framework. Вместо триггера пропускается этот второй шаг, что приводит к ошибке вставки 0 строк.

Я нашел ответ в этой ветке StackOverflow , в котором предлагалось добавить следующую строку в конце вашего триггера (в случае, когда элемент не соответствует и вставка выполняется).

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
3 голосов
/ 12 декабря 2013

У меня был почти точно такой же сценарий: вставки, управляемые Entity Framework в представление с триггером INSTEAD OF INSERT, приводили к исключению «... неожиданное количество строк (0) ...». Благодаря ответу Райана Гросса я исправил это, добавив

SELECT SCOPE_IDENTITY() AS CentrePersonID;

в конце моего триггера, где CentrePersonID - это имя ключевого поля базовой таблицы, которая имеет идентификатор автоинструмента. Таким образом, EF может обнаружить идентификатор вновь вставленной записи.

...