効率的な更新
バッチ処理
EF Core は、すべての更新プログラムを 1 回のラウンドトリップで自動的にバッチ処理することで、ラウンドトリップを最小限に抑えるのに役立ちます。 次の点を考慮してください。
var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();
上記では、データベースからブログを読み込み、その URL を変更してから、2 つの新しいブログを追加します。これを適用するには、2 つの SQL INSERT ステートメントと 1 つの UPDATE ステートメントがデータベースに送信されます。 ブログ インスタンスが追加されたときに、それらを 1 つずつ送信するのではなく、EF Core はこれらの変更を内部的に追跡し、SaveChanges 呼び出されたときに 1 回のラウンドトリップで実行します。
EF が 1 回のラウンドトリップでバッチ処理するステートメントの数は、使用されているデータベース プロバイダーによって異なります。 たとえば、パフォーマンス分析では、4 個未満のステートメントが関係する場合、SQL Server のバッチ処理の効率が一般的に低下することが示されています。 同様に、SQL Server の場合、バッチ処理の利点は約 40 個のステートメントの後に低下するため、EF Core では既定では 1 つのバッチで最大 42 個のステートメントのみが実行され、別のラウンドトリップで追加のステートメントが実行されます。
ユーザーは、これらのしきい値を調整して、パフォーマンスが向上する可能性を実現することもできますが、これらを変更する前に慎重にベンチマークします。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
o => o
.MinBatchSize(1)
.MaxBatchSize(100));
}
関連する場合は ExecuteUpdate と ExecuteDelete を使用する
すべての従業員に昇給を与えたいとします。 EF Core でのこの一般的な実装は、次のようになります。
foreach (var employee in context.Employees)
{
employee.Salary += 1000;
}
await context.SaveChangesAsync();
これは完全に有効なコードですが、パフォーマンスの観点から何を行うかを分析してみましょう。
- 関連するすべての従業員を読み込むには、データベースラウンドトリップが実行されます。これは、給与のみが必要な場合でも、すべての従業員の行データをクライアントに提供します。
- EF Core の変更追跡では、エンティティの読み込み時にスナップショットが作成され、それらのスナップショットがインスタンスと比較されて、変更されたプロパティが見つかります。
- 通常、2 つ目のデータベースラウンドトリップは、すべての変更を保存するために実行されます (一部のデータベース プロバイダーでは、変更が複数のラウンドトリップに分割されることに注意してください)。 このバッチ処理の動作は、更新ごとにラウンドトリップを行うよりもはるかに優れていますが、EF Core は従業員ごとに UPDATE ステートメントを送信し、データベースは各ステートメントを個別に実行する必要があります。
EF Core 7.0 以降では、ExecuteUpdateAsync
メソッドと ExecuteDeleteAsync
メソッドを使用して、同じことをはるかに効率的に行うことができます。
await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));
これにより、次の SQL ステートメントがデータベースに送信されます。
UPDATE [Employees] SET [Salary] = [Salary] + 1000;
この UPDATE
は、データベースに実際のデータを読み込んだり送信したりすることなく、EF の変更追跡機構を使用せずに、1 回のラウンドトリップで操作全体を実行します。これにより、追加のオーバーヘッドが発生します。 詳細については、「ExecuteUpdate
と ExecuteDelete
」を参照してください。
ExecuteUpdate
と ExecuteDelete
をまだサポートしていない古いバージョンの EF Core を使用している場合、またはこれらのメソッドでサポートされていない複雑な SQL ステートメントを実行する場合でも、SQL クエリを使用して操作を実行できます。
context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");
.NET