【C#】Entity Framework Coreでテーブルの行を削除する方法

目次

概要

Entity Framework Core (EF Core) を使用して、データベースから特定のレコード(行)を物理削除する基本的な実装です。 対象のデータを取得して Remove メソッドに渡し、SaveChangesAsync を呼び出すことで、データベースに対して DELETE SQLが発行されます。

仕様(入出力)

  • 入力: 削除対象のデータID(整数)。
  • 出力: 削除処理の結果(成功メッセージまたはエラーメッセージ)。
  • 前提: .NET 6.0以上、Microsoft.EntityFrameworkCore.InMemory(動作確認用)。

基本の使い方

  1. 削除したいデータを取得する。
  2. DbSet.Remove メソッドに対象オブジェクトを渡す。
  3. 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)によっては、親データを消すと子データも自動的に消える場合があります。意図しない削除を防ぐため、外部キー制約の設定を確認してください。

注意点

  1. 取得コスト: Remove を呼ぶためには、原則として一度データをDBからメモリにロード(SELECT)する必要があります。削除のためだけに大量のデータをロードするのはパフォーマンス上不利です(後述の応用を参照)。
  2. Nullチェック: FindAsyncFirstOrDefaultAsync の結果が null の状態で Remove に渡すと例外(または不正な動作)の原因になります。必ず存在確認を行ってください。
  3. 参照整合性エラー: 他のテーブルから参照されているデータを削除しようとすると、データベース側で制約エラーが発生する可能性があります。例外処理(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 の利用を検討してください。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次