Entity Framework Coreとは?.NET向けORMフレームワーク完全ガイド
Entity Framework Coreとは、.NETアプリケーションでデータベース操作を効率化するためのORMフレームワークです。C#のクラスとデータベースのテーブルを対応付けることで、開発者はSQLを直接書き続けなくても、オブジェクト指向の形でデータを取得・追加・更新・削除できます。
ASP.NET CoreでWeb APIや業務アプリケーションを開発する場合、EF Coreは非常によく使われます。DbContext、Entity、Migration、LINQ、Change Trackerなどの仕組みを理解すると、データアクセス層を効率的かつ保守しやすく設計できます。本記事では、EF Coreの基本から実務での使い方、パフォーマンス最適化、セキュリティ、注意点まで体系的に解説します。
1. Entity Framework Coreとは
Entity Framework Coreとは、Microsoftが提供する.NET向けのデータアクセスフレームワークです。C#のEntityクラスとデータベーステーブルを対応させ、LINQを使ってデータを取得したり、オブジェクトの変更をSaveChangesでデータベースへ反映したりできます。
EF Coreの本質は、アプリケーションコードとデータベース操作の橋渡しです。たとえば、Userクラスを作成し、DbContextにUsersというDbSetを定義すれば、C#コードからUsersテーブルを扱えるようになります。これにより、SQL中心ではなく、C#中心でデータアクセスを設計できます。
1.2 ORM(Object-Relational Mapping)の基本概念
ORMとは、Object-Relational Mappingの略で、オブジェクト指向の世界とリレーショナルデータベースの世界をつなぐ仕組みです。C#ではクラスやオブジェクトを使ってデータを表現しますが、データベースではテーブル、行、列としてデータを保存します。ORMはこの違いを吸収します。
ORMを使うと、開発者はSQL文を直接組み立てる代わりに、C#のオブジェクト操作としてデータを扱えます。たとえば、context.Products.Where(p => p.Price > 1000) のようなLINQ式は、EF CoreによってSQLへ変換され、データベースで実行されます。
| 概念 | C#側 | データベース側 |
|---|---|---|
| データ構造 | Class / Object | Table / Row |
| プロパティ | Property | Column |
| 一覧 | DbSet | Table |
| 関係 | Navigation Property | Foreign Key |
| 保存 | SaveChanges | INSERT / UPDATE / DELETE |
1.3 EF Coreが解決する課題
EF Coreが解決する大きな課題は、データアクセスコードの複雑さです。ADO.NETだけで開発する場合、接続を開き、SQLを組み立て、パラメータを設定し、DataReaderで値を読み取り、オブジェクトへ変換する処理を何度も書く必要があります。EF Coreはこれらを抽象化します。
また、EF CoreはMigrationによってデータベーススキーマの変更管理も支援します。モデルを変更したときにMigrationを作成し、データベースへ反映できるため、開発中のスキーマ変更を追跡しやすくなります。チーム開発でも、誰がどの変更を加えたかを管理しやすくなります。
1.4 ADO.NETとの違い
ADO.NETは、SQLを直接実行する低レベルなデータアクセス技術です。柔軟性と制御性は高いですが、コード量が多くなりやすく、SQLとC#オブジェクトの変換を手動で行う必要があります。一方、EF Coreはより高レベルなORMであり、C#のEntityを中心にデータ操作を行います。
どちらが優れているというより、用途が異なります。細かいSQL最適化や特殊な処理ではADO.NETやRaw SQLが有効な場合がありますが、一般的なCRUDや業務アプリ開発ではEF Coreの方が生産性が高くなりやすいです。実務では、基本はEF Coreを使い、必要な箇所だけRaw SQLを併用する設計もよくあります。
| 比較項目 | EF Core | ADO.NET |
|---|---|---|
| 抽象度 | 高い | 低い |
| SQL記述量 | 少ない | 多い |
| 生産性 | 高い | 中〜低 |
| 細かい制御 | やや制限あり | 非常に高い |
| 学習対象 | ORM, LINQ, DbContext | SQL, Connection, Command |
| 適した用途 | 一般的な業務アプリ | 高度なSQL制御、特殊処理 |
1.5 .NETエコシステムにおける役割
EF Coreは、.NETエコシステムにおける標準的なデータアクセス技術の一つです。ASP.NET Core、Blazor、Worker Service、Console App、MAUIアプリのバックエンドなど、多くの.NETアプリケーションで利用できます。Dependency Injectionとも統合しやすく、アプリケーション全体の構造に組み込みやすい点が特徴です。
特にASP.NET Core Web APIでは、ControllerやMinimal APIからServiceを呼び出し、Service内でDbContextを使ってデータベース操作を行う構成が一般的です。EF Coreは単なる便利ライブラリではなく、.NETアプリケーションのデータ層を支える基盤技術として位置付けられます。
1.6 EF Coreの主な特徴
EF Coreの主な特徴は、LINQによるクエリ、Change Tracking、Migration、複数データベース対応、非同期処理、リレーション管理、DI統合、クロスプラットフォーム対応です。これらにより、データベース操作をC#らしい形で記述できます。
また、EF CoreはSQL Serverだけでなく、PostgreSQL、MySQL、SQLite、Oracle、Cosmos DBなどにも対応できます。Providerを切り替えることで複数のデータベースを扱えるため、プロジェクト要件に応じた柔軟な選択が可能です。
2. EF Coreのアーキテクチャ
EF Coreのアーキテクチャは、Entity、DbContext、DbSet、Change Tracker、Query Pipeline、Providerによって構成されます。開発者はC#のEntityを操作し、EF CoreはそれをSQLやデータベース操作へ変換します。
この仕組みを理解せずに使うと、N+1問題、不要なTracking、重いSQL生成、過剰なIncludeなどによってパフォーマンス問題が発生しやすくなります。EF Coreを実務で使うには、内部の流れをある程度理解することが重要です。
2.1 ORMの仕組み
ORMは、アプリケーション内のオブジェクトとデータベース内のテーブルを対応付けます。EF Coreでは、Entityクラスがテーブルに対応し、Entityのプロパティがカラムに対応します。たとえば、ProductクラスのNameプロパティはProductsテーブルのNameカラムに対応します。
データ取得時には、EF CoreがSQLを発行し、取得した行をC#オブジェクトへ変換します。データ更新時には、変更されたEntityをChange Trackerが検出し、SaveChangesが呼ばれたタイミングでINSERT、UPDATE、DELETE文を生成して実行します。
2.2 オブジェクトとテーブルのマッピング
EF Coreでは、C#クラスとデータベーステーブルの対応関係をConvention、Data Annotations、Fluent APIによって定義できます。Conventionでは、クラス名やプロパティ名から自動的にマッピングが推測されます。たとえば、IdやProductIdという名前のプロパティは主キーとして認識されやすいです。
より細かい設定が必要な場合は、Data AnnotationsやFluent APIを使います。テーブル名、カラム名、最大文字数、必須項目、リレーション、インデックスなどを明示的に指定できます。実務では、複雑な設定はFluent APIへ集約することが多いです。
2.3 Entityの役割
Entityは、データベース上の1つのレコードをC#のオブジェクトとして表現するクラスです。たとえば、User、Product、Order、OrderItemなどがEntityになります。Entityには、主キー、通常のプロパティ、外部キー、Navigation Propertyなどを定義します。
Entity設計は、データベース設計とアプリケーション設計の両方に影響します。プロパティを増やしすぎると責務が曖昧になり、リレーションを適切に設計しないとクエリが複雑になります。Entityは単なるデータ入れ物ではなく、ドメイン構造を反映する重要な要素です。
2.4 DbContextの役割
DbContextは、EF Coreにおけるデータベース操作の中心です。データベース接続、Entityの追跡、クエリ実行、変更保存、トランザクション管理などを担当します。アプリケーションはDbContextを通じてデータベースにアクセスします。
DbContextにはDbSetを定義します。たとえば、public DbSet Products { get; set; } のように定義すると、Productsテーブルに対するクエリやCRUD操作を行えるようになります。DbContextは短命に使うことが推奨され、Webアプリでは通常リクエストごとに1インスタンスを使います。
2.5 Change Trackerの仕組み
Change Trackerは、DbContextが取得または追加したEntityの状態を追跡する仕組みです。Entityが追加されたのか、変更されたのか、削除されたのかを管理し、SaveChanges時に必要なSQLを生成します。
Change Trackerは便利ですが、読み取り専用クエリでは不要な場合があります。大量データを取得する読み取り処理でTrackingが有効になっていると、メモリ使用量や処理コストが増えることがあります。そのため、読み取り専用ではAsNoTrackingを使うのが一般的です。
2.6 Query Pipeline
Query Pipelineとは、LINQで書かれたクエリがSQLに変換され、データベースで実行され、結果がEntityやDTOに変換されるまでの処理の流れです。EF CoreはLINQ式を解析し、Providerに応じたSQLを生成します。
この仕組みを理解すると、どのLINQが効率的なSQLに変換されるかを意識できるようになります。複雑すぎるLINQやクライアント側評価が発生する処理は、パフォーマンス低下の原因になるため、生成SQLを確認する習慣が重要です。
3. DbContextの理解
DbContextはEF Coreの中核です。DbContextを理解しないままEF Coreを使うと、ライフサイクル、Tracking、SaveChanges、Transaction、DIスコープで問題が起きやすくなります。DbContextは単なる接続オブジェクトではなく、作業単位を表す重要なクラスです。
実務では、DbContextを適切なスコープで登録し、必要なときにDIから受け取り、処理が終わったら破棄される構成にします。ASP.NET CoreではAddDbContextを使うことで、通常はリクエスト単位のScopedライフタイムとして管理されます。
3.1 DbContextとは
DbContextとは、EF Coreでデータベースとやり取りするためのクラスです。Entityの集合を表すDbSetを持ち、クエリ実行、追加、更新、削除、変更追跡、保存処理を担当します。EF Coreを使うアプリケーションでは、ほぼ必ずDbContextを定義します。
たとえば、AppDbContextというクラスを作り、その中にUsers、Products、OrdersといったDbSetを定義します。これにより、アプリケーションはcontext.Usersやcontext.Productsを通じてデータベースの各テーブルを操作できます。
3.2 DbSetとは
DbSetとは、特定のEntityに対応するテーブルのようなものです。DbSet Productsは、Productsテーブルに対するクエリや追加・削除操作を行う入口になります。LINQを使ってWhere、Select、OrderByなどの処理を記述できます。
DbSetは単なるリストではありません。IQueryableとして動作し、LINQ式はすぐにメモリ上で実行されるのではなく、データベース向けのSQLへ変換されます。そのため、ToListやFirstOrDefaultなどを呼ぶタイミングで実際のクエリが実行されます。
3.3 データベース接続管理
DbContextは、内部的にデータベース接続を管理します。開発者は通常、Connectionを手動で開閉する必要はありません。EF Coreが必要なタイミングで接続を開き、処理が終われば閉じます。
ただし、接続文字列の管理は重要です。appsettings.json、環境変数、Secret Manager、クラウドのシークレット管理機能などを使い、本番環境の接続情報を安全に扱う必要があります。接続文字列をソースコードに直書きするのは避けるべきです。
3.4 Entity管理
DbContextは、Entityの状態を管理します。EntityにはAdded、Unchanged、Modified、Deleted、Detachedなどの状態があります。EF Coreはこれらの状態をもとに、SaveChanges時に実行するSQLを判断します。
たとえば、新しいEntityをAddするとAdded状態になり、既存Entityのプロパティを変更するとModifiedとして検出されます。この状態管理を理解すると、更新されない、不要なUPDATEが発行される、削除が反映されないといった問題を解決しやすくなります。
3.5 SaveChangesの動作
SaveChangesは、Change Trackerが管理している変更をデータベースへ反映するメソッドです。追加されたEntityはINSERT、変更されたEntityはUPDATE、削除されたEntityはDELETEとして処理されます。非同期版としてSaveChangesAsyncもよく使われます。
重要なのは、Entityを変更しただけではデータベースは更新されないという点です。SaveChangesを呼んだタイミングで初めて変更が永続化されます。複数の変更をまとめてSaveChangesすれば、1つの作業単位として扱いやすくなります。
3.6 ライフサイクル管理
DbContextは長期間使い回すべきではありません。長く保持するとChange Trackerに多くのEntityが残り、メモリ使用量が増えたり、意図しない変更が保存されたりする可能性があります。ASP.NET Coreでは、通常1リクエストにつき1つのDbContextを使います。
Background ServiceやConsole Appで使う場合は、DbContextの生成と破棄を明示的に管理する必要があります。DI Scopeを作成し、その中でDbContextを取得して使う設計が安全です。DbContextのライフサイクル管理は、EF Coreの安定運用に直結します。
4. Entity設計
Entity設計は、EF Coreを使ううえで非常に重要です。Entityはアプリケーションのデータ構造を表すだけでなく、データベースのテーブル設計にも影響します。適切なEntity設計ができていないと、クエリが複雑になり、保守性も低下します。
Entity設計では、主キー、外部キー、必須項目、文字数制限、リレーション、命名規則を明確にする必要があります。小さなプロジェクトでも、最初から一貫した設計ルールを持つことで、後から拡張しやすくなります。
4.1 Entityクラスの作成
Entityクラスは、データベースのテーブルに対応するC#クラスです。たとえば、ProductクラスにはId、Name、Price、CreatedAtなどのプロパティを定義します。このクラスがEF Coreによってテーブルへマッピングされます。
Entityクラスは、シンプルで分かりやすく保つことが重要です。すべての処理をEntityに詰め込むのではなく、業務ロジックの一部はServiceやDomain Serviceへ分離することもあります。特に大規模システムでは、Entityの責務を明確にすることが保守性につながります。
4.2 Primary Key設定
Primary Keyは、テーブル内の各レコードを一意に識別するためのキーです。EF Coreでは、IdやEntityNameIdという名前のプロパティはConventionによって主キーとして認識されやすいです。たとえばProductIdやIdがよく使われます。
主キーにはint、long、Guidなどを使えます。小規模アプリではintの自動採番が簡単ですが、分散システムや外部公開IDではGuidが使われることもあります。主キー設計は、データ量、セキュリティ、分散性を考慮して選ぶべきです。
4.3 Data Annotations
Data Annotationsは、Entityのプロパティに属性を付けて制約やマッピングを指定する方法です。Required、MaxLength、Key、Column、Tableなどを使うことで、簡単に設定できます。
Data Annotationsは手軽で読みやすい反面、複雑な設定には向いていません。簡単なバリデーションや制約には便利ですが、複雑なリレーションやインデックス設定はFluent APIで管理する方が見通しがよくなります。
4.4 Fluent API設定
Fluent APIは、DbContextのOnModelCreating内でEntity設定を行う方法です。テーブル名、カラム制約、リレーション、インデックス、複合キー、Delete Behaviorなどを細かく指定できます。
実務では、複雑なマッピングはFluent APIに集約することが多いです。Entityクラスを属性で汚しすぎず、設定をDbContext側またはEntityTypeConfigurationクラスに分離できるため、保守性が高まります。
4.5 Validationルール
Validationルールは、データが正しい形式で保存されるようにするために必要です。たとえば、商品名は必須、価格は0以上、メールアドレスは正しい形式、文字数は最大100文字などのルールを設定します。
EF Coreの制約だけでなく、アプリケーション側のValidationも重要です。データベース制約は最後の防御線であり、ユーザーに分かりやすいエラーメッセージを返すには、ServiceやDTOレベルでのValidationも必要です。
4.6 命名規則
命名規則は、Entity設計の一貫性を保つために重要です。クラス名、テーブル名、プロパティ名、外部キー名、Navigation Property名を統一すると、コードが読みやすくなります。
たとえば、Entity名は単数形、DbSet名は複数形、外部キーはCustomerId、Navigation PropertyはCustomerのように統一すると分かりやすいです。命名規則がバラバラだと、プロジェクトが大きくなるほど理解しづらくなります。
5. Code Firstアプローチ
Code Firstは、C#のEntityクラスを先に作成し、そのモデルからデータベースを生成・更新するアプローチです。新規開発の.NETアプリケーションではよく使われます。コードを中心にデータベース設計を進められるため、開発速度が高い点が特徴です。
Code Firstでは、Entityを変更し、Migrationを作成し、データベースへ反映する流れでスキーマを管理します。モデルとDB構造の変更履歴をコードとして管理できるため、チーム開発でもスキーマ変更を共有しやすくなります。
5.1 Code Firstとは
Code Firstとは、データベースを直接作るのではなく、C#のEntityクラスとDbContextを定義し、それをもとにデータベースを生成する方法です。開発者はコード上でモデルを定義し、EF Core Migrationによってスキーマを作成します。
この方法は、新規プロジェクトやドメインモデルをアプリケーション側で設計したい場合に向いています。データベースよりもアプリケーションコードを中心に設計を進められるため、.NET開発者にとって自然な開発フローになります。
5.2 モデルからDB生成
Code Firstでは、EntityクラスとDbContextの設定をもとにMigrationを作成し、データベースへ適用します。たとえばProductクラスを追加すれば、MigrationによってProductsテーブルを作成できます。
この仕組みにより、モデルの変更とデータベーススキーマの変更を連動させられます。ただし、Migrationを適用する前に生成内容を確認することが重要です。意図しないカラム削除や型変更が含まれていないかチェックする必要があります。
5.3 開発フロー
Code Firstの基本フローは、Entityを作成し、DbContextにDbSetを追加し、Migrationを作成し、データベースへ適用する流れです。その後、アプリケーションからDbContextを使ってCRUD処理を実装します。
開発中は、モデル変更のたびにMigrationを作成することになります。Migration名はAddProductsTableやAddOrderStatusのように、何を変更したか分かる名前にすると、チーム開発で履歴を追いやすくなります。
5.4 メリットとデメリット
Code Firstのメリットは、C#コードを中心に開発できること、Migrationでスキーマ変更を管理できること、新規開発と相性が良いことです。開発者がモデルを変更しながら素早くDB構造を進化させられます。
一方で、既存データベースがすでにある場合や、DBAが厳密にスキーマを管理する環境では使いにくい場合があります。また、Migration運用を誤ると、本番DBに危険な変更を適用してしまう可能性があります。
| 項目 | Code First |
|---|---|
| 向いているケース | 新規開発 |
| 中心 | C#モデル |
| スキーマ管理 | Migration |
| メリット | 開発速度が高い |
| 注意点 | Migration運用に注意 |
5.5 実務での活用
実務では、Code Firstはスタートアップ、新規Web API、SaaS開発、社内業務システムなどでよく使われます。アプリケーション側でモデルを設計し、MigrationによってDBを更新する流れが開発チームに合いやすいためです。
ただし、本番環境ではMigrationを自動適用するか手動適用するかを慎重に決める必要があります。大規模システムでは、Migration SQLを事前確認し、リリース手順の一部としてDB更新を行うことが一般的です。
5.6 ベストプラクティス
Code Firstのベストプラクティスは、Migrationを小さく分けること、Migration名を分かりやすくすること、生成されたMigrationを必ずレビューすることです。特に本番DBへ適用する前には、どのテーブルやカラムが変更されるかを確認します。
また、Entity設計とDB設計を完全に自動任せにしないことも重要です。インデックス、制約、リレーション、削除動作などは、Fluent APIで明示的に設定すると安全です。Code Firstは便利ですが、DB設計の知識が不要になるわけではありません。
6. Database Firstアプローチ
Database Firstは、既存のデータベースからEntityクラスとDbContextを生成するアプローチです。すでに運用中のDBがある場合や、DBAが設計したスキーマにアプリケーションを接続する場合に向いています。
この方法では、EF CoreのScaffold機能を使ってデータベース構造からC#コードを生成します。既存システムのリプレイス、レガシーDBとの連携、業務DBを使った新規API開発などでよく使われます。
6.1 Database Firstとは
Database Firstとは、既存のデータベーススキーマをもとに、EF CoreのEntityやDbContextを生成する方法です。Code Firstとは逆に、データベースが設計の出発点になります。
この方法は、すでにテーブル、カラム、外部キー、ビュー、ストアドプロシージャなどが定義されている環境に適しています。アプリケーション側は、既存DB構造に合わせてデータアクセスを実装します。
6.2 Scaffold機能
Scaffold機能は、既存DBからEntityクラスとDbContextを自動生成する機能です。dotnet ef dbcontext scaffoldコマンドを使い、接続文字列とProviderを指定してコードを生成します。
Scaffoldによって生成されたコードは便利ですが、そのまま使い続けると再生成時に手動修正が上書きされる可能性があります。実務では、生成コードとカスタムコードを分離する設計が重要です。
6.3 既存DBとの連携
既存DBとの連携では、テーブル命名、カラム名、主キー、外部キー、nullable、データ型などを正確に反映する必要があります。古いDBでは命名規則が統一されていない場合もあるため、生成されたEntityが読みづらくなることがあります。
必要に応じて、Fluent APIやpartial classを使って扱いやすい構成に調整します。ただし、DB側の構造を勝手に変更できないケースも多いため、アプリ側でどこまで吸収するかを判断する必要があります。
6.4 モデル生成
Database Firstでは、DBスキーマからモデルが生成されます。テーブルはEntity、カラムはプロパティ、外部キーはNavigation Propertyとして表現されます。これにより、既存DBをC#から扱えるようになります。
ただし、自動生成されたモデルが必ず理想的なドメインモデルになるとは限りません。データベース都合の構造とアプリケーション都合の構造が異なる場合、DTOやService層で変換することが必要になります。
6.5 運用上の注意点
Database Firstでは、DBスキーマが変更されたときにモデルを再生成する必要があります。このとき、手動で追加した変更が上書きされないように注意します。生成コードと手書きコードを分離することが重要です。
また、DB変更の管理主体がアプリチームではなくDBAや別チームの場合、スキーマ変更のタイミングを把握しておく必要があります。アプリ側だけで完結しないため、チーム間の連携が重要です。
6.6 利用シナリオ
Database Firstは、既存DBを使う業務システム、レガシーシステムのAPI化、社内基幹システムとの連携、DBA主導の開発環境に向いています。すでに安定運用されているDBを活かしたい場合に有効です。
一方、新規開発でアプリ側がモデル設計を主導できる場合は、Code Firstの方が開発しやすいこともあります。どちらを選ぶかは、プロジェクトの出発点がコードなのか、データベースなのかによって決まります。
7. Migrationの仕組み
Migrationは、EF Coreでデータベーススキーマの変更履歴を管理する仕組みです。EntityやDbContextの変更をもとにMigrationファイルを作成し、それをデータベースへ適用します。Code First開発では特に重要です。
Migrationを使うことで、スキーマ変更をコードとして管理でき、チームメンバーや環境間で同じDB変更を再現しやすくなります。ただし、Migrationは強力である一方、本番DBに影響を与えるため、慎重な運用が必要です。
7.1 Migrationとは
Migrationとは、データベーススキーマの変更を記録するファイルです。たとえば、新しいテーブルを追加する、カラムを追加する、インデックスを作る、外部キーを変更する、といった変更内容がMigrationとして保存されます。
MigrationにはUpメソッドとDownメソッドがあります。Upは変更を適用する処理、Downは変更を戻す処理です。これにより、スキーマを前後に移動できるようになります。
7.2 Migration作成
Migrationを作成するには、EntityやDbContextを変更した後に、dotnet ef migrations add AddProductTableのようなコマンドを実行します。これにより、現在のモデルと過去の状態との差分がMigrationとして生成されます。
Migration名は非常に重要です。Update1やTestMigrationのような曖昧な名前ではなく、AddUserEmailIndex、CreateOrdersTable、AddProductStatusのように変更内容が分かる名前にするべきです。
7.3 Migration適用
Migrationをデータベースへ適用するには、dotnet ef database updateを使います。これにより、未適用のMigrationが順番に実行され、DBスキーマが最新状態になります。
開発環境ではコマンドで直接適用してもよいですが、本番環境では事前にSQLスクリプトを生成し、レビューしてから適用する方が安全です。特にカラム削除や型変更はデータ損失につながる可能性があります。
7.4 スキーマ更新
スキーマ更新とは、Entityモデルの変更をデータベース構造へ反映することです。たとえば、ProductにDescriptionプロパティを追加した場合、MigrationによってProductsテーブルにDescriptionカラムを追加します。
スキーマ更新では、既存データへの影響を考える必要があります。nullableではないカラムを追加する場合、既存行に対するデフォルト値が必要になることがあります。Migrationは生成されたら必ず中身を確認しましょう。
7.5 Rollback方法
Rollbackは、適用済みMigrationを以前の状態に戻すことです。EF Coreでは、特定のMigrationまでdatabase updateすることで戻せます。Downメソッドが正しく生成されていれば、スキーマを前の状態に戻せます。
ただし、本番環境でRollbackする場合は注意が必要です。カラム削除やデータ変換を伴うMigrationでは、完全に元に戻せないことがあります。スキーマRollbackだけでなく、データバックアップも含めて計画するべきです。
7.6 チーム開発での運用
チーム開発では、複数人が同時にMigrationを作成することがあります。この場合、Migrationの競合や適用順序の問題が起こる可能性があります。Gitのマージ時にはMigrationファイルとModel Snapshotを注意深く確認する必要があります。
実務では、Migration作成ルール、命名規則、レビュー手順、本番適用手順をチームで決めておくことが重要です。Migrationはコードと同じくレビュー対象にし、データベース変更の品質を保つべきです。
8. CRUD操作
EF Coreの基本操作は、Create、Read、Update、DeleteのCRUDです。ほとんどの業務アプリケーションは、これらの操作を組み合わせて構成されます。EF Coreを使うと、CRUDをC#オブジェクト操作として直感的に実装できます。
ただし、単純に動かすだけでなく、非同期処理、Validation、Transaction、Tracking、DTO分離を意識することが実務では重要です。CRUDは簡単に見えますが、品質の差が出やすい部分です。
8.1 Create(追加)
Createは、新しいEntityをDbContextに追加し、SaveChangesでデータベースへ保存する操作です。たとえば、新しいProductを作成し、context.Products.Add(product) を呼び、SaveChangesAsyncで保存します。
実務では、Entityを直接API requestから作るのではなく、CreateProductRequestのようなDTOを受け取り、Validationを行ったうえでEntityへ変換するのが望ましいです。これにより、不要なプロパティの書き換えや不正な入力を防げます。
8.2 Read(取得)
Readは、データベースからデータを取得する操作です。EF Coreでは、LINQを使ってWhere、OrderBy、Select、FirstOrDefault、ToListなどを記述できます。条件検索やページネーションもLINQで表現できます。
読み取り処理では、必要なデータだけを取得することが重要です。Entity全体を取得するのではなく、SelectでDTOへProjectionすると、不要なカラムを取得せずに済み、パフォーマンスが向上します。
8.3 Update(更新)
Updateは、既存Entityの値を変更し、SaveChangesでデータベースへ反映する操作です。TrackingされているEntityを取得し、そのプロパティを変更すれば、EF Coreが変更を検出します。
実務では、更新対象が存在するか、ユーザーに更新権限があるか、同時更新が発生していないかを確認する必要があります。特に複数ユーザーが同じデータを更新する業務システムでは、Concurrency制御が重要になります。
8.4 Delete(削除)
Deleteは、Entityを削除する操作です。context.Products.Remove(product) のように削除対象を指定し、SaveChangesでDELETE文が実行されます。単純な削除は簡単ですが、実務では物理削除と論理削除の選択が重要です。
履歴を残したい業務データでは、DeletedAtやIsDeletedを使った論理削除がよく使われます。物理削除すると過去の注文やレポートとの整合性が崩れる場合があるため、削除方針は業務要件に合わせて決める必要があります。
8.5 非同期CRUD
ASP.NET Coreアプリでは、ToListAsync、FirstOrDefaultAsync、SaveChangesAsyncなどの非同期メソッドを使うのが一般的です。データベースアクセスはI/O処理であり、非同期化することでスレッドを効率的に使えます。
非同期CRUDは、アクセス数が増えるWeb APIで特に重要です。同期処理でスレッドをブロックすると、同時リクエスト処理能力が下がる可能性があります。実務では、EF Coreの非同期APIを標準として使うとよいでしょう。
8.6 Transaction管理
Transactionは、複数のデータベース操作を1つの作業単位として扱う仕組みです。たとえば、注文作成、在庫減少、支払い記録をまとめて成功させる必要がある場合、途中で失敗したらすべてをRollbackする必要があります。
EF Coreでは、通常SaveChangesは1つのTransactionとして実行されます。複数回のSaveChangesや複雑な処理をまとめたい場合は、BeginTransactionを使って明示的にTransactionを管理できます。
9. LINQとEF Core
EF Coreの大きな特徴の一つがLINQとの統合です。LINQを使うことで、C#の構文でデータベースクエリを記述できます。Where、Select、OrderBy、GroupByなどを使い、SQLに近い処理をC#らしく表現できます。
ただし、LINQで書いた処理がすべて効率的なSQLになるとは限りません。EF CoreがSQLへ変換できる式とできない式を理解し、必要に応じて生成SQLを確認することが重要です。
9.1 LINQ統合
LINQ統合により、EF CoreではC#コードとしてクエリを書けます。たとえば、価格が1000円以上の商品を取得する処理は、context.Products.Where(p => p.Price >= 1000) のように表現できます。
この書き方は型安全で、IDEの補完やコンパイルチェックを受けられます。SQL文字列を直接組み立てるよりも、ミスを減らしやすく、リファクタリングにも強い点がメリットです。
9.2 IQueryableの仕組み
IQueryableは、まだ実行されていないクエリを表す仕組みです。WhereやOrderByをつなげても、その時点ではSQLは実行されません。ToList、FirstOrDefault、Countなどを呼んだタイミングで実行されます。
この遅延実行を理解していないと、意図しないタイミングでクエリが実行されたり、メモリ上で処理されてしまったりすることがあります。EF Coreでは、IQueryableのまま条件を組み立て、最後に必要な形で実行するのが基本です。
9.3 SQL変換プロセス
EF CoreはLINQ式を解析し、対象データベースのProviderに応じたSQLを生成します。SQL Server、PostgreSQL、MySQLなど、Providerによって生成されるSQLは異なる場合があります。
実務では、ToQueryStringを使って生成SQLを確認することが有効です。特に複雑なInclude、GroupBy、Join、Projectionを使う場合は、実際にどのようなSQLが発行されているかを見ることで、パフォーマンス問題を早期に発見できます。
9.4 Projection
Projectionとは、Entity全体ではなく、必要な項目だけを選択して取得することです。たとえば、商品一覧画面ではId、Name、Priceだけが必要で、DescriptionやCreatedByなどは不要な場合があります。
Projectionを使うと、取得するカラムを減らせるため、パフォーマンスが向上します。また、API response用のDTOへ直接変換できるため、外部に公開したくないEntity内部情報を隠すこともできます。
9.5 Filtering
Filteringは、Whereを使って条件に合うデータだけを取得する処理です。商品名検索、価格範囲、カテゴリ指定、ステータス絞り込みなど、多くの画面で必要になります。
Filteringでは、条件を動的に組み立てる場面も多いです。検索フォームの入力に応じてIQueryableへWhereを追加していくことで、柔軟な検索APIを作れます。ただし、複雑になりすぎる場合はSpecification Patternなどの導入も検討できます。
9.6 Aggregation
Aggregationは、Count、Sum、Average、Min、Maxなどの集計処理です。Dashboard、レポート、統計画面では頻繁に使われます。EF CoreではLINQの集計メソッドをSQLの集計関数へ変換できます。
集計処理では、データをすべてメモリに読み込んでから計算するのではなく、データベース側で集計することが重要です。たとえば、ToListしてからCountするのではなく、CountAsyncを使う方が効率的です。
10. リレーションシップ管理
EF Coreでは、Entity同士のリレーションを管理できます。One-to-One、One-to-Many、Many-to-Manyなどの関係をNavigation PropertyやForeign Keyで表現します。業務アプリではリレーション設計が非常に重要です。
リレーションを正しく設計すると、関連データの取得や保存が自然に行えます。一方で、設計が曖昧だと、不要なInclude、N+1問題、Cascade Deleteによる予期しない削除などの問題が起きやすくなります。
10.1 One-to-One
One-to-Oneは、1つのEntityが別の1つのEntityと対応する関係です。たとえば、UserとUserProfile、EmployeeとEmployeeDetailのような関係が考えられます。
One-to-Oneは便利ですが、実務では本当にテーブルを分ける必要があるかを検討するべきです。セキュリティ、パフォーマンス、拡張性の理由がある場合は有効ですが、単に項目が多いだけなら同じテーブルでもよい場合があります。
10.2 One-to-Many
One-to-Manyは、1つのEntityが複数のEntityを持つ関係です。たとえば、CategoryとProducts、CustomerとOrders、BlogとPostsが該当します。業務アプリで最もよく使われる関係です。
EF Coreでは、親EntityにCollection Navigation Propertyを持たせ、子EntityにForeign Keyを持たせることで表現します。One-to-Manyを正しく設計できると、多くのデータモデルを自然に扱えるようになります。
10.3 Many-to-Many
Many-to-Manyは、複数対複数の関係です。たとえば、PostとTag、StudentとCourse、UserとRoleなどが該当します。EF Coreでは、中間テーブルを使ってこの関係を表現します。
シンプルなMany-to-ManyならEF Coreが中間テーブルを自動的に扱えますが、中間テーブルに追加情報を持たせたい場合は明示的なEntityを作る必要があります。たとえば、受講登録日やステータスを持つStudentCourseなどです。
10.4 Foreign Key管理
Foreign Keyは、テーブル間の関係を表すキーです。EF Coreでは、外部キーのプロパティとNavigation Propertyを組み合わせてリレーションを管理します。たとえば、OrderにはCustomerIdとCustomerを持たせます。
Foreign Keyを明示的に持たせると、クエリや更新処理が分かりやすくなります。Navigation Propertyだけに頼るよりも、外部キーをコード上で確認できるため、実務では明示的なForeign Keyを定義することが多いです。
10.5 Navigation Property
Navigation Propertyは、Entity同士の関係をC#オブジェクトとしてたどるためのプロパティです。Order.CustomerやCustomer.Ordersのように、関連データへアクセスできます。
Navigation Propertyは便利ですが、読み込み戦略とセットで考える必要があります。関連データが自動で読み込まれるとは限らないため、Eager Loading、Lazy Loading、Explicit Loadingの違いを理解することが重要です。
10.6 Cascade Delete
Cascade Deleteは、親Entityを削除したときに関連する子Entityも自動削除する仕組みです。たとえば、Orderを削除したらOrderItemsも削除する、といったケースで使えます。
ただし、Cascade Deleteは慎重に使う必要があります。意図せず大量の関連データが削除される可能性があるため、業務データではDelete Behaviorを明示的に設定し、論理削除を検討することも多いです。
11. データ読み込み戦略
EF Coreでは、関連データをどのタイミングで読み込むかを選べます。代表的な方法は、Eager Loading、Lazy Loading、Explicit Loadingです。読み込み戦略を理解しないと、パフォーマンス問題が起こりやすくなります。
特にN+1問題は、ORM利用時によく発生する問題です。関連データを必要な分だけ効率よく取得するには、IncludeやProjectionを適切に使う必要があります。
11.1 Eager Loading
Eager Loadingは、最初のクエリで関連データも一緒に読み込む方法です。EF CoreではIncludeを使って、関連Entityをまとめて取得できます。たとえば、OrdersとOrderItemsを同時に取得する場合に便利です。
Eager Loadingは、必要な関連データが明確な場合に有効です。ただし、Includeを増やしすぎるとSQLが複雑になり、取得データ量も大きくなります。必要な関連だけを読み込むことが重要です。
11.2 Lazy Loading
Lazy Loadingは、Navigation Propertyへアクセスしたタイミングで関連データを読み込む方法です。コード上は自然に見えますが、裏側で追加クエリが発行されるため、N+1問題を引き起こしやすいです。
Lazy Loadingは便利ですが、実務では慎重に使うべきです。どのタイミングでSQLが発行されるか見えにくくなるため、パフォーマンスを重視するAPIではEager LoadingやProjectionを使う方が安全です。
11.3 Explicit Loading
Explicit Loadingは、必要なタイミングで明示的に関連データを読み込む方法です。Entry APIを使って、特定のNavigation Propertyをロードします。Lazy Loadingよりも制御しやすい点が特徴です。
たとえば、基本情報を取得した後、特定条件のときだけ関連データを読み込みたい場合に有効です。読み込みタイミングをコードで明示できるため、意図しないクエリ発行を防ぎやすくなります。
11.4 Includeの利用
Includeは、関連Entityを同時に取得するために使います。ThenIncludeを使えば、さらに深い階層の関連データも取得できます。たとえば、Customer、Orders、OrderItemsをまとめて取得できます。
ただし、Includeを多用するとクエリが重くなる可能性があります。画面に必要なデータだけをDTOへProjectionする方が効率的な場合もあります。Includeは便利ですが、常に最適とは限りません。
11.5 N+1問題
N+1問題とは、最初に1回クエリを実行し、その後各レコードごとに追加クエリが発行される問題です。たとえば、100件の注文を取得した後、各注文の明細を個別に読み込むと、合計101回のクエリが発行されます。
N+1問題を避けるには、Includeでまとめて取得する、Projectionで必要なデータを一度に取得する、Lazy Loadingを不用意に使わない、といった対策が必要です。EF Coreのパフォーマンス改善で最も重要なポイントの一つです。
11.6 最適な選択基準
読み込み戦略の選択基準は、必要なデータが事前に分かっているか、データ量がどれくらいか、クエリの複雑さが許容できるかによって変わります。関連データが確実に必要ならEager Loading、条件付きならExplicit Loading、単純なプロトタイプならLazy Loadingも選択肢になります。
実務では、APIレスポンスに必要な形へProjectionする方法が最も安定しやすいです。Entity全体を取得するのではなく、必要な項目だけをDTOとして取得することで、データ量とSQLを制御しやすくなります。
| 読み込み方法 | 特徴 | 注意点 |
|---|---|---|
| Eager Loading | 関連データを最初から取得 | Include過多に注意 |
| Lazy Loading | アクセス時に取得 | N+1問題に注意 |
| Explicit Loading | 明示的に取得 | 実装量が増える |
| Projection | 必要項目だけ取得 | DTO設計が必要 |
12. パフォーマンス最適化
EF Coreのパフォーマンス最適化では、Tracking、Projection、Index、Query、Batch、Cacheを意識します。EF Coreは便利ですが、何も考えずに使うと不要なデータ取得や重いSQLが発生することがあります。
パフォーマンス問題の多くは、取得しすぎ、Trackingしすぎ、Includeしすぎ、クエリを確認していないことから発生します。EF Coreを実務で使うなら、生成SQLと実行計画を確認する習慣を持つことが重要です。
12.1 AsNoTrackingの活用
AsNoTrackingは、取得したEntityをChange Trackerに登録しないためのメソッドです。読み取り専用の一覧表示やレポートでは、Trackingが不要なことが多いため、AsNoTrackingを使うとメモリ使用量と処理コストを削減できます。
ただし、取得したEntityをその後更新する場合はTrackingが必要です。読み取り専用か更新対象かを明確に分け、読み取り専用クエリではAsNoTrackingを標準的に使うとよいでしょう。
12.2 Projection最適化
Projection最適化では、Selectを使って必要なカラムだけを取得します。Entity全体を取得すると、画面で使わないデータまで読み込まれる可能性があります。特に大きなテキスト、画像URL、監査情報などは不要な場合があります。
DTOへ直接Projectionすると、APIレスポンスに必要な形でデータを取得できます。これにより、データ転送量を減らし、セキュリティ面でも不要な内部情報を外部へ出さずに済みます。
12.3 クエリ最適化
クエリ最適化では、Where条件、OrderBy、Include、Join、GroupByの使い方を見直します。複雑なLINQが意図しないSQLを生成している場合、パフォーマンスが大きく低下することがあります。
EF CoreではToQueryStringを使って生成SQLを確認できます。遅いクエリを見つけたら、SQLの内容、インデックス利用、JOIN数、取得カラム、実行計画を確認し、必要に応じてクエリを書き換えます。
12.4 Index活用
Indexは、データベース検索を高速化するための仕組みです。検索条件やソート条件に使われるカラムには、適切なインデックスを作成するとパフォーマンスが向上します。
EF CoreではFluent APIを使ってIndexを定義できます。ただし、Indexを増やしすぎるとINSERTやUPDATEが遅くなる場合があります。読み取り性能と書き込み性能のバランスを考えて設計することが重要です。
12.5 Batch処理
Batch処理では、大量データの追加・更新・削除を効率的に行います。EF Coreで大量のEntityを1件ずつ処理すると、Change TrackerやSaveChangesのコストが大きくなる可能性があります。
大量処理では、一定件数ごとにSaveChangesする、Trackingを抑える、必要に応じてBulk操作ライブラリやRaw SQLを使うなどの工夫が必要です。業務バッチやデータ移行では特に重要なテーマです。
12.6 キャッシュ戦略
キャッシュは、頻繁に参照されるがあまり変わらないデータを一時保存することで、DBアクセスを減らす方法です。カテゴリ一覧、設定値、マスターデータ、Dashboardの集計結果などはキャッシュ候補になります。
ただし、キャッシュには更新タイミングの問題があります。古いデータが表示され続けないように、有効期限や明示的なキャッシュ削除を設計する必要があります。EF Coreそのものだけでなく、MemoryCacheやRedisとの組み合わせも有効です。
13. 高度なクエリ
EF Coreでは、基本的なCRUDだけでなく、GroupBy、Join、SelectMany、Raw SQL、Stored Procedure、Dynamic Queryなどの高度なクエリも扱えます。これらを理解すると、複雑な業務要件にも対応しやすくなります。
ただし、高度なクエリほどSQL変換やパフォーマンスに注意が必要です。LINQで書けるからといって、常に効率的なSQLになるわけではありません。生成SQLの確認とテストが重要です。
13.1 GroupBy
GroupByは、データを特定のキーでグループ化して集計する処理です。たとえば、カテゴリ別の商品数、月別売上、部署別社員数などを集計できます。Dashboardやレポート機能でよく使います。
EF CoreではGroupByがSQLのGROUP BYへ変換される場合があります。ただし、複雑なGroupByやクライアント側処理を含む場合は、期待通りにSQL変換されないこともあるため、生成SQLを確認することが重要です。
13.2 Join
Joinは、複数のテーブルを結合してデータを取得する処理です。EF CoreではNavigation PropertyとIncludeを使うことが多いですが、明示的にJoinを書くこともできます。
Joinを使うと、必要なデータだけを柔軟に取得できます。ただし、リレーションが正しく定義されている場合は、Navigation Propertyを使った方が読みやすいこともあります。可読性とSQL効率のバランスを見て選びます。
13.3 SelectMany
SelectManyは、ネストしたコレクションを平坦化する処理です。たとえば、複数の注文があり、それぞれに注文明細がある場合、すべての注文明細を1つの一覧として取得できます。
SelectManyは強力ですが、慣れないうちは読みにくくなりがちです。複雑な処理では、クエリを分割したり、DTOへProjectionしたりして、可読性を保つことが重要です。
13.4 Raw SQL実行
Raw SQLは、LINQでは表現しにくいクエリや、パフォーマンス上SQLを直接書きたい場合に使います。EF CoreではFromSqlやExecuteSqlを使ってSQLを実行できます。
Raw SQLを使う場合は、SQL Injection対策が非常に重要です。文字列連結でSQLを作るのではなく、必ずパラメータ化されたクエリを使います。Raw SQLは便利ですが、乱用すると保守性が下がるため、必要な箇所に限定するべきです。
13.5 Stored Procedure利用
Stored Procedureは、データベース側に保存された処理です。複雑な集計、既存DBとの連携、パフォーマンス最適化などの理由で使われることがあります。EF CoreからStored Procedureを呼び出すことも可能です。
ただし、Stored Procedureにロジックを寄せすぎると、アプリケーション側のコードと処理が分散し、保守が難しくなる場合があります。DB中心の設計が必要な場面では有効ですが、使いどころを見極める必要があります。
13.6 Dynamic Query
Dynamic Queryは、ユーザー入力や検索条件に応じてクエリを動的に組み立てる方法です。検索画面、管理画面、レポート機能などでよく使われます。IQueryableに条件を追加していく形で実装できます。
Dynamic Queryでは、条件が増えるほどコードが複雑になりがちです。Specification PatternやQuery Objectを使うと、検索条件を整理しやすくなります。また、動的クエリでもSQL Injection対策とパフォーマンス確認は必要です。
14. トランザクション管理
トランザクションは、複数のデータベース操作を1つのまとまりとして扱う仕組みです。すべて成功したらCommitし、途中で失敗したらRollbackします。注文処理、決済処理、在庫更新などで非常に重要です。
EF Coreでは、1回のSaveChangesは通常トランザクションとして扱われます。しかし、複数回のSaveChangesや複数DbContext、外部処理を含む場合は、明示的なトランザクション管理が必要になることがあります。
14.1 Transactionとは
Transactionとは、データの整合性を守るために複数操作を一括で管理する仕組みです。たとえば、注文を作成し、在庫を減らし、支払い記録を追加する処理では、どれか1つだけ成功するとデータ不整合が起きます。
トランザクションを使えば、すべて成功した場合だけ確定し、途中で失敗したら元に戻せます。業務システムでは、データ整合性を守るために欠かせない考え方です。
14.2 BeginTransaction
BeginTransactionは、明示的にトランザクションを開始するために使います。複数のSaveChangesを1つのトランザクションにまとめたい場合や、複雑な処理を制御したい場合に利用します。
ただし、トランザクションを長時間保持すると、ロックが長引き、他の処理に影響を与える可能性があります。トランザクション内では必要なDB操作だけを行い、外部API呼び出しなど時間のかかる処理は避けるべきです。
14.3 CommitとRollback
Commitは、トランザクション内の変更を確定する処理です。すべての操作が成功した場合にCommitを行います。一方、Rollbackは変更を取り消す処理で、途中で例外が発生した場合などに使います。
実務では、try-catch-finallyを使って、成功時にCommit、失敗時にRollback、最後にリソース破棄を行う構成が一般的です。例外発生時に中途半端なデータが残らないようにすることが重要です。
14.4 Distributed Transaction
Distributed Transactionは、複数のデータベースや外部リソースにまたがるトランザクションです。たとえば、複数DBを同時に更新する場合や、別サービスとの整合性を保証したい場合に検討されます。
ただし、Distributed Transactionは複雑で、クラウドやマイクロサービス環境では扱いにくい場合があります。近年では、Outbox PatternやSaga Patternなどを使って最終的整合性を実現する設計もよく使われます。
14.5 同時実行制御
同時実行制御は、複数ユーザーが同じデータを同時に更新する場合の競合を管理する仕組みです。EF Coreでは、Concurrency TokenやRowVersionを使って楽観的同時実行制御を実装できます。
たとえば、2人の管理者が同じ商品情報を編集した場合、後から保存した人が前の変更を上書きしてしまう可能性があります。同時実行制御を使えば、競合を検出し、ユーザーに再確認を促せます。
14.6 エラーハンドリング
トランザクション処理では、エラーハンドリングが非常に重要です。途中で例外が発生した場合、Rollbackを行い、ログを記録し、ユーザーには適切なエラーメッセージを返す必要があります。
また、データベース例外には一意制約違反、外部キー制約違反、タイムアウト、デッドロックなどがあります。これらをすべて同じエラーとして扱うのではなく、原因に応じて適切に処理することが実務では重要です。
15. セキュリティ対策
EF Coreを使う場合でも、セキュリティ対策は必須です。ORMを使っているから安全というわけではありません。SQL Injection、接続文字列の漏洩、過剰なデータ公開、権限チェック不足などに注意する必要があります。
特にWeb APIでは、ユーザー入力をもとに検索や更新を行うため、Validation、認可、データ保護を組み合わせることが重要です。EF Coreは安全なコードを書きやすくしますが、設計次第では脆弱性が生まれます。
15.1 SQL Injection対策
SQL Injectionは、悪意ある入力によってSQL文を改ざんされる攻撃です。EF CoreでLINQを使っている場合、多くのクエリはパラメータ化されるため、SQL Injectionリスクを下げられます。
ただし、Raw SQLを使う場合は注意が必要です。ユーザー入力を文字列連結でSQLに埋め込むと危険です。必ずパラメータ化された方法を使い、入力値をSQL文字列へ直接連結しないようにします。
15.2 Parameterized Query
Parameterized Queryは、SQL文と値を分離して扱う方法です。これにより、ユーザー入力がSQL構文として解釈されるのを防げます。EF CoreのLINQクエリは基本的にこの仕組みによって安全に変換されます。
Raw SQLを使う場合でも、パラメータを使って値を渡すべきです。検索条件やIDなど、外部入力が関わる値はすべてパラメータ化することが原則です。
15.3 データ保護
データ保護では、個人情報、認証情報、機密データを適切に扱う必要があります。EF Coreで取得したEntityをそのままAPIレスポンスとして返すと、不要な内部情報まで公開される可能性があります。
そのため、APIではDTOを使い、外部に返す項目を明示的に制御することが重要です。パスワードハッシュ、内部メモ、管理用フラグなどを誤って返さないように設計します。
15.4 接続文字列管理
接続文字列には、データベースサーバー、ユーザー名、パスワードなどの重要情報が含まれる場合があります。これをソースコードや公開リポジトリに書くのは危険です。
開発環境ではSecret Manager、本番環境では環境変数、Azure Key Vault、AWS Secrets Managerなどのシークレット管理サービスを使うべきです。appsettings.jsonに本番パスワードを直接書かないようにします。
15.5 シークレット管理
シークレット管理では、DBパスワード、APIキー、JWT secret、外部サービスの認証情報などを安全に管理します。EF Coreそのものの機能ではありませんが、データアクセスに関わる重要な運用ポイントです。
シークレットは環境ごとに分け、開発、ステージング、本番で異なる値を使うべきです。また、不要になったシークレットはローテーションし、アクセス権を最小限にすることが重要です。
15.6 セキュアコーディング
セキュアコーディングでは、入力検証、認可チェック、エラー情報の制御、ログの扱い、データ公開範囲を意識します。EF Coreでデータを取得する前に、ユーザーがそのデータへアクセスできるか確認する必要があります。
また、例外メッセージをそのままクライアントへ返すと、テーブル名やカラム名などの内部情報が漏れる可能性があります。詳細なエラーはログに記録し、ユーザーには安全で分かりやすいメッセージを返す設計が望ましいです。
16. EF CoreとASP.NET Core
EF CoreはASP.NET Coreと非常に相性が良いです。Dependency Injection、Configuration、Logging、Authentication、Web API、Identityなどと自然に統合できます。ASP.NET Coreアプリのデータアクセス層として広く利用されています。
実務では、ControllerやMinimal APIから直接DbContextを使うより、Service層やRepository層を通じてデータ操作を行うことが多いです。アプリケーションの規模に応じて適切な構成を選ぶことが重要です。
16.1 Dependency Injection統合
ASP.NET Coreでは、DbContextをDIコンテナに登録して利用できます。AddDbContextを使うと、通常はScopedライフタイムでDbContextが管理され、リクエストごとに1つのインスタンスが使われます。
DI統合により、Controller、Service、RepositoryでDbContextをコンストラクタ注入できます。これにより、依存関係が明確になり、テストや保守がしやすくなります。
16.2 Repository Pattern
Repository Patternは、データアクセス処理を抽象化するためのパターンです。Serviceが直接DbContextを扱うのではなく、UserRepositoryやProductRepositoryを通してデータを取得・保存します。
ただし、EF Core自体がRepositoryとUnit of Workのような役割を持っているため、すべてのプロジェクトでRepository Patternが必要とは限りません。大規模システムやテスト容易性を重視する場合に導入を検討するとよいでしょう。
16.3 Unit of Work
Unit of Workは、複数の変更を1つの作業単位として管理する考え方です。EF CoreのDbContextは、Change TrackerとSaveChangesによってUnit of Work的な役割を持ちます。
そのため、別途Unit of Workクラスを作るべきかはプロジェクト次第です。Clean Architectureや複雑な業務ロジックでは明示的にUnit of Workを導入する場合もありますが、単純なCRUDアプリではDbContextだけで十分なこともあります。
16.4 Web API連携
ASP.NET Core Web APIでは、EF Coreを使ってデータベース操作を行い、結果をJSONとして返します。このとき、Entityを直接返すのではなく、DTOへ変換して返す設計が推奨されます。
DTOを使うことで、APIレスポンスの形を安定させ、内部Entityの変更が外部APIに直接影響しないようにできます。また、必要な項目だけを返すため、セキュリティとパフォーマンスの両方で有利です。
16.5 Identity統合
ASP.NET Core Identityは、ユーザー管理や認証を提供する仕組みです。Identityは内部でEF Coreを使ってユーザー、ロール、クレーム、ログイン情報などをデータベースに保存できます。
IdentityとEF Coreを組み合わせることで、ログイン機能、ユーザー管理、ロールベース認可を実装しやすくなります。ただし、Identityのテーブル構造や拡張方法を理解しておくことが重要です。
16.6 Clean Architecture
Clean Architectureでは、ビジネスロジックをインフラ層から分離します。EF Coreはインフラ層に配置し、ドメイン層やアプリケーション層がEF Coreに直接依存しすぎないように設計します。
この構成により、テスト容易性と保守性が向上します。ただし、小規模アプリで過剰に層を増やすと複雑になるため、プロジェクト規模に応じたバランスが重要です。
17. サポートされるデータベース
EF Coreは、複数のデータベースに対応できます。SQL Server、PostgreSQL、MySQL、SQLite、Oracle、Cosmos DBなど、Providerを通じてさまざまなデータストアと連携できます。
ただし、Providerによって対応機能やSQL変換の挙動が異なる場合があります。データベースを選ぶ際は、プロジェクト要件、運用環境、パフォーマンス、ライセンス、チーム経験を考慮する必要があります。
17.1 SQL Server
SQL Serverは、Microsoftエコシステムと相性の良いリレーショナルデータベースです。EF Coreとの統合も強く、ASP.NET Coreアプリではよく使われます。Azure SQL Databaseを使えば、クラウド環境でも運用しやすいです。
企業向けシステム、業務アプリ、Windows Server環境、Microsoft 365やAzureとの連携が多いプロジェクトでは、SQL Serverは有力な選択肢になります。
17.2 PostgreSQL
PostgreSQLは、高機能なオープンソースRDBMSです。JSON、全文検索、拡張機能、複雑なクエリに強く、近年のWebアプリやSaaSでも広く使われています。EF CoreではNpgsql Providerを使って接続できます。
PostgreSQLは、オープンソースでありながら高度な機能を持つため、コストと機能のバランスが良い選択肢です。Linux環境やクラウドネイティブな構成とも相性が良いです。
17.3 MySQL
MySQLは、Webアプリケーションで長く使われているリレーショナルデータベースです。LAMP環境や多くのホスティング環境で利用されており、実績が豊富です。EF CoreからもProviderを通じて利用できます。
MySQLは、シンプルなWebサービス、CMS、業務アプリ、既存システムとの連携で使われることがあります。Providerの選定とバージョン互換性を確認して使うことが重要です。
17.4 SQLite
SQLiteは、軽量なファイルベースのデータベースです。サーバー不要で使えるため、ローカルアプリ、テスト、プロトタイプ、モバイルアプリに向いています。EF Coreの学習用としても非常に使いやすいです。
ただし、大規模な同時書き込みや複雑な運用には向きません。本番のWebアプリで多くのユーザーを扱う場合は、SQL ServerやPostgreSQLなどのサーバー型DBを検討するべきです。
17.5 Oracle
Oracle Databaseは、大規模エンタープライズシステムで使われることが多い商用データベースです。金融、公共、基幹システムなどで採用されるケースがあります。EF CoreからもProviderを使って接続できます。
Oracleを使う場合は、既存DBや企業標準に合わせることが多いです。SQL方言、データ型、パフォーマンス設計、ライセンスなどを考慮する必要があります。
17.6 Cosmos DB
Cosmos DBは、Microsoft Azureのグローバル分散型NoSQLデータベースです。EF CoreはCosmos DB Providerを通じて接続できますが、リレーショナルDBとは考え方が異なります。
Cosmos DBを使う場合は、ドキュメント指向、Partition Key、スループット、グローバル分散などを理解する必要があります。EF Coreで扱えるからといって、RDBMSと同じ設計で使うべきではありません。
18. EF Coreのメリット
EF Coreのメリットは、開発効率、型安全性、LINQ統合、Migration、複数DB対応、ASP.NET Coreとの統合性です。データベース操作をC#らしく書けるため、.NET開発者にとって自然な開発体験を提供します。
特にCRUD中心の業務アプリやWeb APIでは、EF Coreを使うことで開発速度が大きく向上します。SQLを完全に知らなくても使い始められますが、実務ではSQLとDB設計の知識も合わせて学ぶことで、より効果的に使えます。
18.1 開発効率向上
EF Coreを使うと、SQLの記述量やデータ変換コードを大幅に減らせます。Entityを作成し、DbContextを通じて操作するだけで、基本的なCRUDを実装できます。
これにより、開発者はデータアクセスの定型処理ではなく、業務ロジックに集中できます。特に短期間でプロトタイプや業務アプリを作る場合、EF Coreの生産性は大きなメリットです。
18.2 型安全性
EF Coreでは、LINQとC#の型システムを使ってクエリを書けます。文字列SQLと異なり、プロパティ名の変更や型の不一致はコンパイル時に検出されやすくなります。
型安全性により、リファクタリングもしやすくなります。たとえば、Product.NameをProduct.Titleへ変更した場合、関連するコードがコンパイルエラーとして検出されるため、修正漏れを見つけやすくなります。
18.3 LINQ活用
LINQを使えることは、EF Coreの大きな魅力です。Where、Select、OrderBy、GroupBy、JoinなどをC#コードとして書けるため、SQLに慣れていない開発者でも扱いやすいです。
また、LINQはメモリ上のコレクション処理にも使えるため、データベースクエリとアプリ内処理の表現を統一しやすくなります。ただし、EF Coreで使うLINQはSQLへ変換されることを意識する必要があります。
18.4 クロスプラットフォーム対応
EF Coreはクロスプラットフォーム対応であり、Windows、Linux、macOS上で動作する.NETアプリケーションから利用できます。Dockerやクラウド環境でも使いやすい点が特徴です。
ASP.NET Coreと組み合わせれば、Linuxコンテナ上でWeb APIを動かし、PostgreSQLやSQL Serverへ接続する構成も一般的です。現代的なクラウドネイティブ開発にも対応しやすい技術です。
18.5 Migration管理
Migrationにより、データベーススキーマの変更をコードとして管理できます。モデル変更の履歴をGitで追跡できるため、チーム開発や環境構築で役立ちます。
新しい環境を作るときも、Migrationを適用すれば同じDBスキーマを再現できます。これは、開発環境、テスト環境、本番環境の整合性を保つうえで大きなメリットです。
18.6 Microsoft公式サポート
EF CoreはMicrosoft公式の.NETデータアクセス技術であり、ASP.NET Coreや.NETの進化に合わせて継続的に改善されています。公式ドキュメント、サンプル、コミュニティ情報も豊富です。
公式サポートがあるため、企業システムでも採用しやすいです。長期的に.NETを使うチームにとって、EF Coreは安定した選択肢の一つになります。
19. EF Coreのデメリット
EF Coreには多くのメリットがありますが、デメリットもあります。ORMの抽象化によるオーバーヘッド、複雑なSQLへの対応、隠れたSQL生成、学習コスト、パフォーマンス調整の必要性などが挙げられます。
EF Coreを使えばSQLを完全に意識しなくてよい、という考え方は危険です。実務で安定したアプリを作るには、EF Coreの仕組み、生成SQL、データベース設計、インデックス、トランザクションを理解する必要があります。
19.1 ORMのオーバーヘッド
EF Coreは便利な抽象化を提供しますが、その分オーバーヘッドがあります。Change Tracking、Entity変換、LINQ解析、SQL生成などの処理が発生するため、手書きSQLより遅くなる場面もあります。
通常の業務アプリでは問題にならないことも多いですが、大量データ処理や高頻度アクセスでは注意が必要です。必要に応じてAsNoTracking、Projection、Raw SQL、Bulk処理を使い分けることが重要です。
19.2 複雑なSQLの制約
EF CoreのLINQは強力ですが、すべてのSQL表現を自然に書けるわけではありません。複雑なウィンドウ関数、特殊なDB関数、Provider固有機能などは、LINQだけでは扱いにくい場合があります。
そのような場合は、Raw SQLやStored Procedureを使う選択肢があります。ただし、EF Coreの抽象化から外れる部分が増えるため、保守性とのバランスを考える必要があります。
19.3 パフォーマンス調整が必要
EF Coreは自動的にSQLを生成しますが、常に最適なSQLを生成するわけではありません。Includeの多用、不要なTracking、大量データ取得、N+1問題などによってパフォーマンスが低下することがあります。
実務では、生成SQLを確認し、遅いクエリをログで検出し、インデックスやProjectionを調整する必要があります。EF Coreを使う場合でも、DBパフォーマンスの知識は不可欠です。
19.4 学習コスト
EF Coreは使い始めるだけなら簡単ですが、実務で正しく使うには学ぶことが多いです。DbContext、Tracking、Migration、LINQ変換、リレーション、Loading戦略、Transaction、Concurrencyなどを理解する必要があります。
特に初心者は、LINQがいつSQLとして実行されるのか、Trackingが何をしているのか、SaveChangesがどのように動くのかでつまずきやすいです。小さなプロジェクトで一つずつ確認しながら学ぶのが効果的です。
19.5 隠れたSQL生成
EF CoreではLINQからSQLが自動生成されるため、実際にどのSQLが実行されているか見えにくい場合があります。見た目は短いLINQでも、裏側では複雑なSQLが発行されていることがあります。
この問題を避けるには、ログやToQueryStringを使ってSQLを確認する習慣が必要です。特にパフォーマンスが重要な画面では、LINQだけを見て安心せず、実際のSQLと実行計画を見るべきです。
19.6 大規模システムでの注意点
大規模システムでは、EF Coreの使い方を標準化することが重要です。各開発者が自由にIncludeやTrackingを使うと、パフォーマンスや保守性にばらつきが出ます。クエリ設計、Repository方針、DTO変換、Migration運用をチームで統一する必要があります。
また、大量データ、複雑な集計、マイクロサービス、複数DB、分散トランザクションなどが絡む場合は、EF Coreだけで解決しようとしないことも重要です。Raw SQL、Dapper、CQRS、イベント駆動設計などを組み合わせる判断も必要になります。
まとめ
Entity Framework Coreは、.NET開発における重要なORMフレームワークです。C#のEntityとデータベーステーブルを対応付け、LINQを使ってデータを取得し、Change TrackerとSaveChangesによって変更を保存できます。ASP.NET Coreとの統合性も高く、Web APIや業務アプリケーションのデータアクセス層として広く利用されています。
EF Coreの中心となる概念は、Entity、DbContext、DbSet、Migration、LINQ、Change Trackerです。これらを理解すると、CRUD操作、リレーション管理、スキーマ更新、トランザクション、パフォーマンス最適化を体系的に扱えるようになります。
一方で、EF Coreは万能ではありません。ORMのオーバーヘッド、隠れたSQL生成、N+1問題、複雑なSQLの制約、Migration運用の難しさなどに注意が必要です。便利な抽象化を使いつつ、SQLとデータベース設計の基本を理解することが、実務でEF Coreを使いこなすための鍵です。
これからEF Coreを学ぶなら、まずは小さなCRUDアプリを作り、DbContext、DbSet、Migration、LINQ、SaveChangesの流れを理解するのがおすすめです。その後、リレーション、Include、AsNoTracking、Projection、Transaction、Repository Pattern、ASP.NET Core Web API連携へ進むと、実務に近いスキルを段階的に身につけられます。
最終的にEF Coreを正しく使うには、「C#で簡単にDBを扱える便利な道具」としてだけでなく、「SQL生成とデータベース設計を理解しながら使うORM」として捉えることが重要です。開発効率と性能のバランスを取りながら設計できれば、EF Coreは.NET開発において非常に強力な武器になります。
EN
JP
KR