web-dev-qa-db-ja.com

EF6で1,000行を削除するにはどうすればよいですか?

Entity Framework 6を​​使用しています。

テストと呼ばれるテスト情報の表があります。最初にテストのリストを取得して、このテーブルから行を削除し、それぞれに対して削除を実行してから、コミットします。

   var testList = _testService.GetTests(1, userId).ToList();
   testList.ForEach(_obj => _uow.Tests.Delete(_obj));
   _uow.Commit();

質問と呼ばれる質問情報を含む別のテーブルがあります。同じことをしたいのですが、このテーブルには1000行以上あります。それらをすべてリストしてから1,000回の削除を行うと、あまり効率的ではありません。

この質問の削除は、あまり頻繁には発生しません。誰が私がこれを行うことができるかについての提案を持っていますか? 1,000回の削除を行う必要があります。 EFを使用してこの種のことを行うのは正常ですか?

39

私の知る限り、EF 6はDbContextに.RemoveRange()オプションを導入しました。つまり、次のようなことができます。

_var db = new MyDbContext();
var itemsToDelete = db.MyTable.Where(x=>!x.active);
db.MyTable.RemoveRange(itemsToDelete);
db.SaveChanges();
_

したがって、任意のタイプのforeachを実行する代わりに、この新しい拡張メソッドを利用できます。作業単位コンテキストを使用すると、現在のメソッドのような単一のDeleteオブジェクトの代わりにIEnumerable(?*)を取るTestメソッドのオーバーロードを作成できます。この新しいオーバーロードは、DbContextでRemoveRange()関数を呼び出す必要があります。

?*-GetTests()が返すものに依存しますが、_IEnumerable<>_は_IList<>_と_IQueryable<>_の両方をカバーすると思います

編集

いくつかのコメント。最初に、RemoveRangeを発行する前に.ToList()を呼び出しません。実際にサービスにアイテムを取得したくないためです。これにより、パフォーマンスの時間を短縮できます。第二に、あなたはまだ1000のdeleteステートメントを発行するということです。ただし、DbSetから削除する個々のアイテムごとに、EFでChangeTrackerを呼び出さないことでパフォーマンスが向上します。 MSDNマガジン から:

AddRangeとRemoveRange前述のように、AddRangeとRemoveRangeはコミュニティメンバーZorrillaからの貢献です。各メソッドは、単一のエンティティタイプの列挙可能なパラメーターをパラメーターとして受け取ります。共有DbTransactionsセクションの最初のコードサンプルでは、​​カジノインスタンスの配列を渡すときにAddRangeを使用しました。

context.Casinos.AddRange(new [] {casino1、casino2});デフォルトでは、Entity Frameworkは各AddおよびRemoveメソッドでDetectChangesを呼び出すため、これらのメソッドは一度に1つのオブジェクトを追加または削除するよりもはるかに高速に実行されます。 Rangeメソッドを使用すると、DetectChangesが1回だけ呼び出されている間に複数のオブジェクトを処理できるため、パフォーマンスが大幅に向上します。 5個、50個、500個、5,000個、さらには50,000個のオブジェクトを使用してこれをテストしました。少なくとも私のシナリオでは、配列のサイズに制限はなく、驚くほど高速です。この改善は、コンテキストをオブジェクトに作用させることにのみ関連し、SaveChangesとは関係がないことに注意してください。 SaveChangesの呼び出しは、一度に1つのデータベースコマンドのみを実行します。したがって、50,000個のオブジェクトをコンテキストにすばやく追加できますが、SaveChangesを呼び出すと、おそらく50,000個の挿入コマンドが個別に実行されます。おそらく、実際のシステムでやりたいことではありません。

これとは逆に、オブジェクトをEF(bit.ly/16tMHw4)で追跡する必要のない一括操作のサポート、およびデータベースへの単一の呼び出しで複数のコマンドを一緒に送信できるようにするバッチ操作のサポートについて、長い議論がありました。 (bit.ly/PegT17)。どちらの機能もEF6の初期リリースには含まれていませんが、両方とも重要であり、将来のリリースで予定されています。

本当に単一のデータベースコマンドのみを発行する場合は、EntityFrameworkがバルクトランザクションをサポートしていないため、生のSQLステートメントを使用するストアドプロシージャを使用する方法があります。ただし、RemoveRangeおよびAddRangeアイテムを使用すると(特に、先ほど述べたように、頻度が低い場合)、foreachループでRemove()を呼び出すのに比べて時間を大幅に節約できます。 。

86
Tommy

ビルトインEntity Framework。RemoveRange()メソッド、まだメモリ上のエントリをフェッチし、[〜#〜 ] x [〜#〜]はすべてのループを削除します。

特に削除するエンティティを選択するときに、削除用のSQLを記述したくない場合は、複雑です

Entity Framework Plus Libraryオファーbatch delete-update1つのコマンドのみを発行するメソッド。

// Deleting
context.Users
  .Where(u => u.FirstName == "firstname")
  .Delete();

Entity Frameworkの現在の制限は、エンティティを更新または削除するには、まずそれをメモリに取得する必要があるということです。現在、ほとんどのシナリオではこれで十分です。ただし、パフォーマンスが低下するセネリオがいくつかあります。また、1回の削除の場合、オブジェクトを削除する前にオブジェクトを取得してから、データベースへの2回の呼び出しが必要です。バッチの更新と削除により、エンティティを変更する前に取得およびロードする必要がなくなります。

12

EF6とSql Server Profilerを使用してテストを行いました

。RemoveRange()を使用

最初にデータベースから削除するすべてのレコードをフェッチします

exec sp_executesql N'SELECT [Extent1]。[Id] AS [Id]、[Extent1]。[IdOrder] AS [IdOrder]、[Extent1]。[Name] AS [Name]、[Extent1]。[Partita] AS [ Partita]、FROM [dbo]。[MyTable] AS [Extent1] WHERE [Extent1]。[IdOrder] = @ p__linq__0 '、N' @ p__linq__0 varchar(8000) '、@ p__linq__0 =' 0cb41f32-7ccb-426a-a159- b85a4ff64c29 '

次に、データベースへのN deleteコマンドを起動します

exec sp_executesql N'DELETE [dbo]。[MyTable] WHERE([Id] = @ 0) '、N' @ 0 varchar(50) '、@ 0 =' ffea29aa-8ba5-4ac9-871b-3f5979180006 '

X 1000回

これもIQueriableを使用して起こります

Entity Framework拡張ライブラリを使用

データベースに対して1つのコマンドのみを実行します

exec sp_executesql N'DELETE [dbo]。[MyTable] FROM [dbo]。[MyTable] AS j0 INNER JOIN(SELECT 1 AS [C1]、[Extent1]。[Id] AS [Id] FROM [dbo]。[MyTable] ] AS [Extent1] WHERE [Extent1]。[IdOrder] = @ p__linq__0)AS j1 ON(j0。[Id] = j1。[Id]) '、N' @ p__linq__0 nvarchar(36) '、@ p__linq__0 = N' 0cb41f32-7ccb-426a-a159-b85a4ff64c29 '

3
Marco Staffoli