entity-framework-core について
.NET 開発者向けの Entity Framework Core クイックリファレンススキルです。DbContext、エンティティ設定、マイグレーション、LINQ クエリに加え、クエリ性能の指針と問題の調査方法を扱います。
主な機能
- 01DbContext の設定
- 02Fluent API によるエンティティ設定
- 03データベースのマイグレーションコマンド
- 04LINQ クエリと射影
- 05読み取り専用処理と一括操作の最適化
- 06よくある問題のトラブルシューティング
活用シーン
- 01データベースコンテキストとエンティティの設定
- 02マイグレーションによるデータベース更新
- 03読み取り専用クエリと一括操作の調整
MCP 対応
`mcp__documentation__fetch_docs` で Entity Framework Core の詳細ドキュメントを取得できると記載されています。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill entity-framework-coreインストール後、Claude Code で/entity-framework-coreを入力して読み込まれたか確認します。
詳細
Entity Framework Core - クイックリファレンス
詳細な知識:テクノロジーに
entity-framework-coreを指定して、mcp__documentation__fetch_docsを使用すると、包括的なドキュメントを参照できます。
DbContext のセットアップ
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<User> Users => Set<User>();
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
// Registration
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
エンティティの構成(Fluent API)
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(u => u.Id);
builder.Property(u => u.Name).IsRequired().HasMaxLength(100);
builder.Property(u => u.Email).IsRequired().HasMaxLength(255);
builder.HasIndex(u => u.Email).IsUnique();
// Relationships
builder.HasMany(u => u.Orders)
.WithOne(o => o.User)
.HasForeignKey(o => o.UserId)
.OnDelete(DeleteBehavior.Cascade);
// Value conversion
builder.Property(u => u.Status)
.HasConversion<string>();
// Default values
builder.Property(u => u.CreatedAt)
.HasDefaultValueSql("GETUTCDATE()");
}
}
マイグレーション
# Add migration
dotnet ef migrations add InitialCreate
# Update database
dotnet ef database update
# Remove last migration (not applied)
dotnet ef migrations remove
# Generate SQL script
dotnet ef migrations script
# Revert to specific migration
dotnet ef database update MigrationName
LINQ クエリ
// Basic queries
var user = await context.Users.FindAsync(id);
var users = await context.Users.Where(u => u.IsActive).ToListAsync();
var user = await context.Users.FirstOrDefaultAsync(u => u.Email == email);
// Projection
var dtos = await context.Users
.Where(u => u.IsActive)
.Select(u => new UserResponse(u.Id, u.Name, u.Email))
.ToListAsync();
// Include related data
var usersWithOrders = await context.Users
.Include(u => u.Orders)
.ThenInclude(o => o.OrderItems)
.ToListAsync();
// Pagination
var page = await context.Users
.OrderBy(u => u.Name)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
// Aggregation
var count = await context.Users.CountAsync(u => u.IsActive);
var avgAge = await context.Users.AverageAsync(u => u.Age);
リポジトリパターン
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
void Update(T entity);
void Remove(T entity);
Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate);
Task SaveChangesAsync();
}
public class Repository<T> : IRepository<T> where T : class
{
protected readonly AppDbContext _context;
protected readonly DbSet<T> _dbSet;
public Repository(AppDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T?> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);
public void Update(T entity) => _dbSet.Update(entity);
public void Remove(T entity) => _dbSet.Remove(entity);
public async Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate)
=> await _dbSet.AnyAsync(predicate);
public async Task SaveChangesAsync() => await _context.SaveChangesAsync();
}
パフォーマンスのヒント
| ヒント | 実装 |
|---|---|
読み取り専用の場合は AsNoTracking() を使用する | context.Users.AsNoTracking().ToListAsync() |
投影には Select を使用する | エンティティ全体の読み込みを避ける |
AsSplitQuery() を使用する | Include による直積の爆発を防ぐ |
| コンパイル済みクエリを使用する | 頻繁に実行する処理には EF.CompileAsyncQuery(...) を使用する |
| バッチ処理を行う | ExecuteUpdateAsync / ExecuteDeleteAsync(EF Core 7+) |
// Bulk update (EF Core 7+)
await context.Users
.Where(u => u.LastLoginAt < cutoff)
.ExecuteUpdateAsync(u => u.SetProperty(x => x.IsActive, false));
// Bulk delete
await context.Users
.Where(u => u.IsDeleted)
.ExecuteDeleteAsync();
アンチパターン
| アンチパターン | 問題点 | 適切な方法 |
|---|---|---|
| 表示のためにエンティティ全体を読み込む | メモリの浪費、処理の遅延 | Select による投影を使用する |
| N+1 クエリ | パフォーマンスを著しく低下させる | Include または投影を使用する |
AsNoTracking を使用しない | 不要なオーバーヘッドが発生する | 読み取り専用クエリで使用する |
エンティティごとに SaveChanges を呼び出す | バッチ処理が遅くなる | すべての変更後に一度だけ呼び出す |
DbContext をシングルトンとして使用する | スレッドセーフではない | AddDbContext(Scoped)を使用する |
簡単なトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| トラッキングの競合 | エンティティはすでにトラッキングされている | AsNoTracking を使用するか、デタッチする |
| マイグレーションの失敗 | モデルが一致していない | 未適用の変更を確認し、再ビルドする |
| クエリが遅い | インデックスがない | 構成に HasIndex を追加する |
| 遅延読み込みの失敗 | 構成されていない | Include(明示的な読み込み)を使用する |
| 同時実行の競合 | データが古い | [ConcurrencyCheck] または RowVersion を追加する |
コメント
Markdown 対応