1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EFCore から Dapper への変更検討してみる

Last updated at Posted at 2025-10-23

はじめに

業務アプリの中で、DB アクセス層のパフォーマンスが気になってきたのがきっかけで記事にしてみました。
単純な CRUD だけならいいのですが、JOIN や集計が増えるとクエリが重くなることがあり、もっと軽くできないか?と思ったわけです。

EntityFrameworkCore の特徴と課題

LINQ でクエリを書けるので、SQL を詳しく知らなくてもデータ操作ができるのが最大の利点だと思います。

ただし、以下のような場面では少しストレスを感じます。

  • トラッキングのオーバーヘッドが重い
  • GroupBy や Include を多用すると SQL が複雑化して最適化されにくい
  • 実際どんなクエリが書かれているか確認しづらい

生産性は高いですが、パフォーマンスをシビアに求める環境では厳しいこともあると思います。

Dapper とは?

Dapper はマイクロORMと呼ばれるカテゴリ軽量ライブラリです。
ORM というよりは SQL マッピングヘルパーに近い存在で、自分で書いた SQL を .NET オブジェクトにマッピングしてくれます。

特徴をざっくり挙げるとこんな感じ

  • SQL は自分で書く(最適化しやすい)
  • ORM 的なトラッキングはなし(その分速い)
  • シンプルなAPI(Query<T>() / Execute()など)

要するに、SQL は自分で書くけど、その分高速ってことです。

パフォーマンス比較

「Dapper は速い」みたいな話、よく聞くと思います。
実際に自身の環境で軽く計測してみました。

計測環境

項目 内容
CPU Intel Core i7-14700K
メモリ 32GB(4400 [MT/s])
DB SQL Server 2022(ローカル)
.NET 9.0
ORM EntityFrameworkCore 9.0 / Dapper 2.1.66

テーブル構造は単純な社員情報を持つEmployeesテーブル(約10万件)を想定。

image.png

単純な SELECT

EFCore

using var context = new AppDBContext();
var employee = await context.Employees.ToListAsync();

Dapper

using var connection = new SqlConnection(ConnectionStr);
var employee = await connection.QueryAsync<EmployeeEntity>("SELECT * FROM Employees");

結果

ORM 実行時間
EFCore 約 530 ms
Dapper 約 120 ms

→約 4 倍速い
Dapper は毎回ほぼ同じ速度で安定。EFCore はコンテキスト生成やトラッキングのオーバーヘッドが効いているのではないかと思います。

JOIN + 条件付き SELECT

EFCore

using var context = new AppDbContext();
var list = await context.Employees
    .Include(e => e.Department)
    .Where(e => e.Salary > 5000000)
    .ToListAsync();

Dapper

using var connection = new SqlConnection(ConnectionStr);
var sql = @"SELECT e.*, d.Name AS DepartmentName 
            FROM Employees e 
            INNER JOIN Departments d ON e.DepartmentId = d.Id
            WHERE e.Salary > @MinSalary";
var list = await connection.QueryAsync<EmployeeEntity>(sql, new { MinSalary = 5000000 });

結果

ORM 実行時間
EFCore 約 650 ms
Dapper 約 180 ms

JOIN が絡んでも Dapper が圧勝。
SQL を自分で書く分、最適化の自由度が高いです。
Include は便利ですが、裏側の SQL がどうなっているのか見えにくいのがネック。

INSERT (1000件追加)

EFCore

using var context = new AppDbContext();
context.Employees.AddRange(newEmployees);
await context.SaveChangesAsync();

Dapper

using var connection = new SqlConnection(ConnectionStr);
var sql = "INSERT INTO Employees (Name, Age, DepartmentId, Salary) VALUES (@Name, @Age, @DepartmentId, @Salary)";
await connection.ExecuteAsync(sql, newEmployees);

結果

ORM 実行時間
EFCore 約 780 ms
Dapper 約 290 ms

またもや Dapper が速いですね。

EFCore が一括挿入非対応という構造的な違いも影響していると思います。
高速化を考えるなら、EFCore.BulkExtensionsを使うのが現実的かも。

AsNoTracking()を使用してトラッキングしないようにもできますが、Entity を通している場合、Update() はできなくなってしまう点には注意してください。

結論

内容 EFCore Dapper コメント
単純SELECT ✗ 遅い ◎ 速い トラッキング不要ならDapperが有利
JOIN付きSELECT △ 普通 ◎ 速い SQLチューニングがしやすい
INSERT(バッチ) △ 普通 ○ 速い EFは拡張ライブラリ前提
メンテナンス性 ◎ 高い △ 手間あり チーム開発ではEFも悪くない

結局のところ、

  • Dapperは「速くてシンプル」
  • EF Coreは「楽で安全」

という印象です。
個人的には、性能が重要な箇所だけDapperに置き換えるハイブリッド構成が一番現実的だと感じました。

設計面から見た検討ポイント

  • Repository / UnitOfWork パターンの扱い
  • Entity の再利用( DTO 分離 or 使い回し)
  • クエリの配置場所(リポジトリ内直書き or SQLファイル管理)

Dapper に変えると、「設計で迷う余地」が増えるかなと思いました。
自由度が高い分、ルールを明確にしておかないとすぐスパゲッティになっちゃいそうです。

採用判断のまとめ

観点 Dapperを選ぶべきケース
パフォーマンス重視 解析・ログ・大量バッチ処理系
SQLを細かく最適化したい 複雑な結合・分析系
チームにSQLに強い人がいる Dapper を活かしやすい環境

まとめ

  • Dapper は EFCore の約 3 ~ 4倍の速度差を確認
  • クエリを自分で最適化する手間は増える
  • チーム開発なら、ハイブリッド構成もおすすめ

最終的には、「性能」よりも「チームの体力」と「保守方針」で決めるのが適切かなという結論に至りました。

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?