基本的な最適化戦略
Entity Framework (EF) はデータアクセス層の開発効率を向上させますが、適切な設定がなければパフォーマンス低下を招きます。以下に主要な改善手法を解説します。
遅延ロードの制御
遅延ロードはデフォルトで有効ですが、不要な場合は無効化し、必要な関連データは明示的に取得します。
var context = new SchoolDbContext();
context.Configuration.LazyLoadingEnabled = false;
// 関連データを含めない場合
var students = context.Students.Take(10).ToList();
// 明示的に関連データを取得
var studentsWithClass = context.Students
.Include(s => s.Class)
.Take(10)
.ToList();
トラッキング無効化による読み取り最適化
読み取り専用クエリではAsNoTrackingを活用し、オブジェクトトラッキングのオーバーヘッドを排除します。
var readOnlyStudents = context.Students
.Where(s => s.Age > 18)
.AsNoTracking()
.ToList();
複数条件ソートの実装
複数カラムのソートではOrderByの連続使用ではなくThenByを適用します。
var sortedStudents = context.Students
.OrderBy(s => s.LastName)
.ThenBy(s => s.FirstName)
.ToList();
SQL直接実行時の分頁処理
サーバー側で分頁処理を行い、メモリ内での処理を避けてください。
var sql = @"
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY CreatedAt DESC) AS RowNum
FROM Students
) AS T
WHERE RowNum BETWEEN 1 AND 20";
var pagedData = context.Database.SqlQuery<Student>(sql).ToList();
存在確認の効率化
存在チェックにはAnyメソッドを使用し、CountやFirstOrDefaultを避けてパフォーマンスを向上させます。
bool exists = context.Classes.Any(c => c.Name == "AdvancedClass");
複数テーブル結合の最適化
内結合と左外部結合の実装例を示します。
// 内結合
var innerJoin = context.Classes
.Where(c => c.Budget > 2000)
.Join(context.Students,
c => c.Id,
s => s.ClassId,
(c, s) => new { ClassName = c.Name, StudentName = s.Name })
.ToList();
// 左外部結合
var leftJoin = (from c in context.Classes
join s in context.Students on c.Id equals s.ClassId into temp
from s in temp.DefaultIfEmpty()
select new { ClassName = c.Name, StudentName = s?.Name })
.ToList();
動的クエリ生成
条件に応じた動的クエリ構築にはExpression拡張を利用します。
Expression<Func<Student, bool>> filter = s => true;
if (!string.IsNullOrEmpty(searchTerm)) {
filter = filter.And(s => s.Name.Contains(searchTerm));
}
if (minAge > 0) {
filter = filter.And(s => s.Age >= minAge);
}
var results = context.Students
.Where(filter)
.OrderBy(s => s.CreatedAt)
.Skip(0)
.Take(20)
.ToList();