目次
概要
Entity Framework Core (EF Core) を使用して、データベースから特定のレコード(行)を物理削除する基本的な実装です。 対象のデータを取得して Remove メソッドに渡し、SaveChangesAsync を呼び出すことで、データベースに対して DELETE SQLが発行されます。
仕様(入出力)
- 入力: 削除対象のデータID(整数)。
- 出力: 削除処理の結果(成功メッセージまたはエラーメッセージ)。
- 前提: .NET 6.0以上、Microsoft.EntityFrameworkCore.InMemory(動作確認用)。
基本の使い方
- 削除したいデータを取得する。
DbSet.Removeメソッドに対象オブジェクトを渡す。SaveChangesAsyncで確定する。
// 1. 削除対象を取得
var item = await context.Items.FindAsync(targetId);
if (item != null)
{
// 2. 削除マークを付ける
context.Items.Remove(item);
// 3. データベースにDELETE文を発行
await context.SaveChangesAsync();
}
コード全文
ここでは「古いシステムログをID指定で削除する」というシナリオのコンソールアプリケーションです。 動作確認のためにインメモリデータベースを使用しています。
外部ライブラリとして Microsoft.EntityFrameworkCore.InMemory が必要です。
dotnet add package Microsoft.EntityFrameworkCore.InMemory
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
public class Program
{
public static async Task Main()
{
var options = new DbContextOptionsBuilder<SystemLogContext>()
.UseInMemoryDatabase("LogDb")
.Options;
// --- 1. 初期データの準備 ---
using (var context = new SystemLogContext(options))
{
if (!await context.SystemLogs.AnyAsync())
{
context.SystemLogs.Add(new SystemLog { Id = 1, Message = "Server started", CreatedAt = DateTime.Now.AddHours(-2) });
context.SystemLogs.Add(new SystemLog { Id = 2, Message = "Connection warning", CreatedAt = DateTime.Now.AddHours(-1) });
await context.SaveChangesAsync();
}
}
// --- 2. 削除処理の実行 ---
using (var context = new SystemLogContext(options))
{
var worker = new LogCleaner(context);
// ID: 2 のログを削除
await worker.DeleteLogAsync(2);
}
// --- 3. 結果の確認 ---
using (var context = new SystemLogContext(options))
{
var logs = await context.SystemLogs.ToListAsync();
Console.WriteLine($"[現在のログ件数]: {logs.Count}件");
foreach (var log in logs)
{
Console.WriteLine($" - ID:{log.Id} {log.Message}");
}
}
}
}
// 業務ロジッククラス
public class LogCleaner
{
private readonly SystemLogContext _context;
public LogCleaner(SystemLogContext context)
{
_context = context;
}
public async Task DeleteLogAsync(int logId)
{
// 削除対象を検索
var log = await _context.SystemLogs.FindAsync(logId);
if (log == null)
{
Console.WriteLine($"エラー: ID {logId} のログは見つかりませんでした。");
return;
}
// 削除状態に設定(この時点ではDBは変更されない)
_context.SystemLogs.Remove(log);
// 変更を確定(DELETE文の発行)
await _context.SaveChangesAsync();
Console.WriteLine($"成功: ID {logId} のログを削除しました。");
}
}
// エンティティ定義
public class SystemLog
{
public int Id { get; set; }
public string Message { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
}
// DbContext定義
public class SystemLogContext : DbContext
{
public SystemLogContext(DbContextOptions<SystemLogContext> options)
: base(options) { }
public DbSet<SystemLog> SystemLogs => Set<SystemLog>();
}
実行結果例
成功: ID 2 のログを削除しました。
[現在のログ件数]: 1件
- ID:1 Server started
カスタムポイント
- 範囲削除: 複数のデータを一度に削除したい場合は、
RemoveRange(IEnumerable<TEntity>)を使用します。C#var oldLogs = context.SystemLogs.Where(x => x.CreatedAt < DateTime.Now.AddDays(-30)); context.SystemLogs.RemoveRange(oldLogs); - 論理削除: データを物理的に消さず「削除フラグ」を立てるだけの場合は、
Removeではなく、Update処理としてIsDeleted = trueに更新する実装に変更してください。 - 関連データの削除: リレーションの設定(
OnDelete: Cascade)によっては、親データを消すと子データも自動的に消える場合があります。意図しない削除を防ぐため、外部キー制約の設定を確認してください。
注意点
- 取得コスト:
Removeを呼ぶためには、原則として一度データをDBからメモリにロード(SELECT)する必要があります。削除のためだけに大量のデータをロードするのはパフォーマンス上不利です(後述の応用を参照)。 - Nullチェック:
FindAsyncやFirstOrDefaultAsyncの結果が null の状態でRemoveに渡すと例外(または不正な動作)の原因になります。必ず存在確認を行ってください。 - 参照整合性エラー: 他のテーブルから参照されているデータを削除しようとすると、データベース側で制約エラーが発生する可能性があります。例外処理(
DbUpdateException)を実装するか、依存関係を適切に整理してください。
応用
EF Core 7.0以降では、データを取得せずにデータベース上で直接削除を実行する ExecuteDeleteAsync が使用可能です。 ネットワーク転送量が減り、非常に高速です。
// 取得(SELECT)をスキップして、条件に一致するデータを直接DELETEする
int deletedCount = await _context.SystemLogs
.Where(x => x.Id == 2)
.ExecuteDeleteAsync();
Console.WriteLine($"{deletedCount} 件削除しました。");
まとめ
- 基本的な削除は「取得」→「Remove」→「SaveChangesAsync」の手順で行います。
- 削除処理を行う際は、必ず対象が存在するかどうかのNullチェックを行ってください。
- パフォーマンスが重要な一括削除などの場面では、EF Core 7以降の
ExecuteDeleteAsyncの利用を検討してください。
