7


1

OptimisticConcurrencyException-Entity Frameworkでトリガーを挿入する代わりにSQL 2008 R2

SQL 2008 R2 Novemberリリースデータベースと.net 4.0 Beta 2 Azureワーカーロールアプリケーションを使用します。 workerロールはデータを収集し、1つのID列を持つ単一のSQLテーブルに挿入します。 このワーカーロールのインスタンスが複数実行されている可能性が高いため、SQLテーブルにInsert Of Ofトリガーを作成しました。 トリガーは、SQLマージ機能を使用してアップサート機能を実行します。 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

2 Answer


7


ご想像のとおり、問題は、Identity列を持つテーブルへの挿入の直後にscope_identity()の選択が続き、Entity Frameworkに関連する値を設定することです。 トリガーの代わりにこの2番目のステップが欠落し、0行の挿入エラーが発生します。

https://stackoverflow.com/questions/5820992/error-when-inserting-into-table-having-instead-of-trigger-from-entity-data-framew/6298870#6298870 [この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


3


ほぼ同じシナリオがありました:INSTANCEAD OF INSERTトリガーを持つビューへのEntity Framework駆動の挿入は、「…​ unexpected number of rows(0)…​」例外を発生させていました。 Ryan Grossの回答のおかげで、以下を追加して修正しました

SELECT SCOPE_IDENTITY() AS CentrePersonID;

トリガーの最後で、 `CentrePersonID`は、自動強化IDを持つ基になるテーブルのキーフィールドの名前です。 このようにして、EFは新しく挿入されたレコードのIDを発見できます。