7

Uso de una base de datos de versiones de noviembre de SQL 2008 R2 y una aplicación de rol de trabajo .net 4.0 Beta 2 Azure. El rol de trabajador recopila datos y los inserta en una sola tabla SQL con una columna de identidad. Como es probable que haya varias instancias de este rol de trabajador ejecutándose, creé un desencadenador Insertar en lugar de en la tabla SQL. El desencadenador realiza la funcionalidad Upsert utilizando la función SQL Merge. Utilizando T-SQL, pude verificar la inserción en lugar de las funciones de desencadenar correctamente, se insertaron nuevas filas mientras se actualizaban las filas existentes. Este es el código para mi gatillo:OptimisticConcurrencyException - SQL 2008 R2 en lugar del desencadenador de inserción con Entity Framework

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 

Dentro del rol de trabajo que estoy utilizando Entity Framework para un modelo de objetos. Cuando llamo el método SaveChanges I receieve la siguiente excepción:

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. 

Entiendo que esto es debido a likly SQL no informar de un IdentityScope para cada nueva fila insertada/actualizada. Entonces EF cree que las filas no se insertaron y la transacción no se compromete en última instancia.

¿Cuál es la mejor forma de manejar esta excepción? ¿Tal vez usando OUTPUT de la función de combinación de SQL?

Gracias! -Paul

Respuesta

7

Como sospechaba, el problema es que las inserciones en una tabla con una columna Identity son seguidas inmediatamente por una selección de scope_identity() para rellenar el valor asociado en Entity Framework. El disparador en lugar del desencadenador hace que se pierda este segundo paso, lo que lleva al error de 0 filas insertadas.

Encontré una respuesta en this StackOverflow thread que sugería agregar la siguiente línea al final de su desencadenador (en el caso de que el elemento no coincida y se realice la inserción).

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

He probado esto con Entity Framework 4.1, y me ha solucionado el problema. He copiado aquí toda mi creación de disparadores para completar. Con este argumento defensivo, pude agregar filas a la tabla agregando entidades de Dirección al contexto y guardándolas usando 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

Y si lo estás haciendo EN LUGAR DE ACTUALIZAR, simplemente asegúrate de "ESTABLECER NOCOUNT OFF" justo antes de tu instrucción INSERT, y no realices ningún tipo de selección después de ella. –

+0

Debería marcarse como respuesta –

3

que tenían casi exactamente el mismo escenario: inserciones Entity Framework-conducido a una vista con un disparador INSTEAD OF INSERT en él se traduce en el "... inesperado número de filas (0) ..." es una excepción. Gracias a la respuesta de Ryan bruto que fija añadiendo

SELECT SCOPE_IDENTITY() AS CentrePersonID; 

al final de mi gatillo, donde CentrePersonID es el nombre del campo clave de la tabla subyacente que tiene una identidad de auto-inrcementing. De esta forma, el EF puede descubrir la ID del registro recién insertado.

+0

Acabo de descubrir que, incluso si los nombres de campo no distinguen entre mayúsculas y minúsculas, el nombre del campo codificado arriba _es_ distingue entre mayúsculas y minúsculas y debe coincidir exactamente con la propiedad Key Key code code correspondiente . –

Cuestiones relacionadas