Entity Framework Tutorial EF追加パフォーマンスを向上させる
Entity Frameworkのパフォーマンスを向上させる方法
複数のエンティティに対してAdd()メソッドを使いすぎると、アプリケーションでパフォーマンスの問題が発生します。
using (var ctx = new CustomerContext()) { foreach(var line in lines) { var customer = new Customer(); // ...code... ctx.Customers.Add(customer); } ctx.SaveChanges(); }
StackOverflow関連の質問
回答
Entity Frameworkの追加は本当に遅いですか?
実際、 Addメソッドはまったく遅くありません。エンティティをリストに追加するのはそれほど遅くはできません。それはめちゃくちゃ遅いAddメソッドの中で呼び出されるDetectChangesメソッドです!
ループ内でAddメソッドを使用することは、通常は不適切な方法であり、使用頻度が低いとアプリケーションのパフォーマンスに大きな影響を与えます。
- USE AddRange over追加( 推奨 )
- falseに設定 AutoDetectChanges
- SPLIT SaveChangesを複数のバッチに分けて
追加でAddRangeを使用する(推奨)
複数のエンティティを追加するときは、Addメソッドを複数回呼び出すのではなく、常にEntity FrameworkのAddRangeをリストと共に1回使用する必要があります。
どうして?
- Addメソッドは、レコードが追加されるたびにDetectChangesを検出します。
- AddRangeメソッドDetectChangeは、すべてのレコードが追加された後に検出します。
性能比較
オペレーション | 100エンティティ | 1,000エンティティ | 10,000エンティティ |
---|---|---|---|
追加する | 15ミリ秒 | 1,050ミリ秒 | 105,000ミリ秒 |
AddRange | 1ミリ秒 | 10ミリ秒 | 120ミリ秒 |
注意:
- * :SaveChanges時間は含まれていません
- ** :二つの関係を持つエンティティ
どうやって?
- リストを作成する
- リストにエンティティを追加
- リスト付きUSR AddRange
- 変更内容を保存
- 完了しました。
using (var context = new EntityContext()) { // 1. CREATE a list List<Customer> list = new List<Customer>(); for(int i = 0; i < 2000; i++) { var customer = new Customer(); // ...code... // 2. ADD entity to the list list.Add(customer); } // 3. USE AddRange with the list context.Customers.AddRange(list); // 4. SaveChanges ctx.SaveChanges(); // 5. Done! }
AutoDetectChangesをfalseに設定
複数のエンティティを追加するときにAddRangeを使用できない場合は、Entity FrameworkのAutoDetectChangesをfalseに設定します。
どうして?
- Addメソッドは、レコードが追加されるたびにDetectChangesを検出します。
AutoDetectChangesを無効にすると、DetectChangesメソッドはユーザーが実行したときにのみ呼び出されます。
性能比較
オペレーション | 100エンティティ | 1,000エンティティ | 10,000エンティティ |
---|---|---|---|
True(デフォルト) | 15ミリ秒 | 1,050ミリ秒 | 105,000ミリ秒 |
偽 | 1ミリ秒 | 14ミリ秒 | 180ミリ秒 |
注意:
- * :SaveChanges時間は含まれていません
- ** :二つの関係を持つエンティティ
どうやって?
- SET AutoDetectChangesEnabled = false
- SaveChangesの前にDetectChangesを呼び出す
- 変更内容を保存
- 完了しました。
using (var context = new EntityContext()) { // 1. SET AutoDetectChangesEnabled = false context.Configuration.AutoDetectChangesEnabled = false; List<Customer> list = new List<Customer>(); for(int i = 0; i < 2000; i++) { var customer = new Customer(); // ...code... list.Add(customer); } context.Customers.AddRange(list); // 2. CALL DetectChanges before SaveChanges context.ChangeTracker.DetectChanges(); // 3. SaveChanges context.SaveChanges(); // 4. Done! }
SPLIT SaveChangesを複数のバッチに分割
この解決策はお勧めできません。複数のエンティティを追加するときは、複数の異なるコンテキストでバッチサイズのエンティティを分割します。
どうして?
コンテキストに含まれる追跡エンティティが多いほど、DetectChangesメソッドの速度は遅くなります。したがって、コンテキストによってエンティティの数を減らすことで、パフォーマンスが向上します。
性能比較
オペレーション | 100エンティティ | 1,000エンティティ | 10,000エンティティ |
---|---|---|---|
無制限 | 15ミリ秒 | 1,050ミリ秒 | 105,000ミリ秒 |
10年 | 3ミリ秒 | 40ミリ秒 | 350ミリ秒 |
100 | 15ミリ秒 | 125ミリ秒 | 1,200ミリ秒 |
1000 | 15ミリ秒 | 1,050ミリ秒 | 10,200ミリ秒 |
注意:
- * :SaveChanges時間は含まれていません
- ** :二つの関係を持つエンティティ
どうやって
- batchSize変数を作成する
- 新しいバッチを作成する前にSaveChangesを呼び出します。
- SaveChangesを呼び出す
- 完了しました。
// 1. CREATE a batchSize variable int batchSize = 400; var context = new EntityContext(); for(int i = 0; i <= 2000; i++) { // 2. CALL SaveChanges before creating a new batch if (i != 0 && i%batchSize == 0) { context.SaveChanges(); context = new EntityContext(); } var customer = new Customer(); // ...code... context.Customers.Add(customer); } // 3. CALL SaveChanges context.SaveChanges(); // 4. Done!