演習 - 移行を設定する
このユニットでは、ローカル環境の SQLite データベースのテーブルにマップされる、C# のエンティティ クラスを作成します。 それらのエンティティから、EF Core 移行機能でテーブルを生成します。
移行によって、データベース スキーマを増分更新する方法が提供されます。
プロジェクト ファイルを取得する
作業を開始するには、プロジェクト ファイルを取得します。 プロジェクト ファイルを取得する方法には、いくつかのオプションがあります。
- GitHub Codespaces を使用する
- GitHub リポジトリをクローンする
互換性のあるコンテナー ランタイムがインストールされている場合は、Dev Containers 拡張機能を使って、ツールがプレインストールされたコンテナーでリポジトリを開くこともできます。
GitHub Codespaces を使用する
codespace は、クラウドでホストされている IDE です。 GitHub Codespaces を使用している場合は、ブラウザーでリポジトリに移動します。 [コード] を選択し、main
ブランチに新しい codespace を作成します。
GitHub リポジトリをクローンする
GitHub Codespaces を使用していない場合は、プロジェクトの GitHub リポジトリをクローンしてから、ファイルを Visual Studio Code でフォルダーとして開くことができます。
コマンド ターミナルを開き、コマンド プロンプトを使用して GitHub からプロジェクトをクローンします。
git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
mslearn-persist-data-ef-core フォルダーに移動し、Visual Studio Code でプロジェクトを開きます。
cd mslearn-persist-data-ef-core code .
コードの確認
作業するプロジェクト ファイルが作成されたので、プロジェクトの内容を確認し、コードを確認しましょう。
- ASP.NET Core Web API プロジェクトは ContosoPizza ディレクトリにあります。 このモジュールで参照するファイル パスは、ContosoPizza ディレクトリに相対的です。
- Services/PizzaService.cs は、作成、読み取り、更新、削除 (CRUD) のメソッドを定義するサービス クラスです。 現在は、すべてのメソッドが
System.NotImplementedException
をスローします。 - Program.cs では、
PizzaService
が ASP.NET Core の依存関係挿入システムに登録されています。 - Controllers/PizzaController.cs は
ApiController
の値であり、HTTP POST、GET、PUT、DELETE 動詞用のエンドポイントを公開しています。 これらの動詞によって、PizzaService
上の対応する CRUD メソッドが呼び出されます。PizzaService
は、PizzaController
コンストラクターに挿入されます。 - Models フォルダーには、
PizzaService
とPizzaController
で使用されるモデルがあります。 - エンティティ モデル Pizza.cs、Topping.cs、Sauce.cs の間には、次のようなリレーションシップがあります。
- 1 つのピザで、1 つ以上のトッピングを使用できます。
- 1 つのトッピングを、1 つ以上のピザで使用できます。
- 1 つのピザで使用できるソースは 1 種類ですが、1 種類のソースを多くのピザで使用できます。
アプリのビルド
Visual Studio Code でアプリをビルドするには:
[エクスプローラー] ペインで ContosoPizza ディレクトリを右クリックし、[統合ターミナルで開く] を選択します。
ContosoPizza ディレクトリをスコープとするターミナル ペインが開きます。
次のコマンドを使用してアプリをビルドします。
dotnet build
コードは、警告やエラーなしでビルドされるはずです。
NuGet パッケージと EF Core ツールを追加する
このモジュールで使用するデータベース エンジンは SQLite です。 SQLite は、軽量のファイルベースのデータベース エンジンです。 これは開発とテストに適しており、小規模な運用環境のデプロイにも適しています。
注意
前述のように、EF Core のデータベース プロバイダーはプラグ可能です。 SQLite は軽量でクロスプラットフォームであるため、このモジュールに適しています。 同じコードを使用して、SQL Server や PostgreSQL などのさまざまなデータベース エンジンを操作できます。 同じアプリで複数のデータベース エンジンを使用することもできます。
始める前に、必要なパッケージを追加します。
ターミナル ペインで、次のコマンドを実行します。
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
このコマンドでは、EF Core SQLite データベース プロバイダーとそのすべての依存関係 (一般的な EF Core サービスなど) を含む NuGet パッケージを追加します。
次に、このコマンドを実行します。
dotnet add package Microsoft.EntityFrameworkCore.Design
このコマンドを実行すると、EF Core ツールに必要なパッケージが追加されます。
完了するには、次のコマンドを実行します。
dotnet tool install --global dotnet-ef
このコマンドを実行すると、移行とスキャフォールディングの作成に使用する
dotnet ef
ツールがインストールされます。ヒント
dotnet ef
が既にインストールされている場合は、dotnet tool update --global dotnet-ef
を実行して更新できます。
モデルと DbContext をスキャフォールディングする
次に、DbContext
の実装を追加して構成します。 DbContext
は、データベースとの対話を可能にするゲートウェイです。
ContosoPizza ディレクトリを右クリックし、Data という新しいフォルダーを追加します。
Data フォルダーに、PizzaContext.cs という名前の新しいファイルを作成します。 次のコードを空のファイルに追加します。
using Microsoft.EntityFrameworkCore; using ContosoPizza.Models; namespace ContosoPizza.Data; public class PizzaContext : DbContext { public PizzaContext (DbContextOptions<PizzaContext> options) : base(options) { } public DbSet<Pizza> Pizzas => Set<Pizza>(); public DbSet<Topping> Toppings => Set<Topping>(); public DbSet<Sauce> Sauces => Set<Sauce>(); }
上のコードでは以下の操作が行われます。
- コンストラクターは、
DbContextOptions<PizzaContext>
型のパラメーターを受け取ります。 コンストラクターによって外部コードから構成を渡すことができるので、テスト コードと運用コードで同じDbContext
を共有でき、異なるプロバイダーでも使用できます。 DbSet<T>
プロパティは、データベース内に作成するテーブルに対応します。- テーブル名は、
PizzaContext
クラスのDbSet<T>
プロパティの名前と一致します。 必要に応じて、この動作をオーバーライドできます。 PizzaContext
をインスタンス化すると、Pizzas
、Toppings
、Sauces
の各プロパティが公開されます。 それらのプロパティによって公開されるコレクションに対して行った変更は、データベースに反映されます。
- コンストラクターは、
Program.cs で、
// Add the PizzaContext
を次のコードに置き換えます。builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
上記のコードでは次の操作が行われます。
PizzaContext
を、ASP.NET Core 依存関係挿入システムに登録します。PizzaContext
で SQLite データベース プロバイダーを使用することを指定します。- ローカル ファイル ContosoPizza.db を指し示す SQLite 接続文字列を定義します。
Note
SQLite はローカル データベース ファイルを使用するため、接続文字列をハードコーディングしても "問題ありません"。 PostgreSQL や SQL Server のようなネットワーク データベースの場合は、常に接続文字列を安全に格納する必要があります。 ローカル開発の場合は、Secret Manager を使用します。 運用環境のデプロイでは、Azure Key Vault のようなサービスの使用を検討します。
また、Program.cs で、
// Additional using declarations
を次のコードに置き換えます。using ContosoPizza.Data;
このコードは、前のステップの依存関係を解決します。
すべての変更を保存します。 GitHub Codespaces によって、変更が自動的に保存されます。
dotnet build
を実行して、ターミナルでアプリをビルドします。 ビルドは警告やエラーなしで成功するはずです。
移行を作成して実行する
次に、初期データベースの作成に使用できる移行を作成します。
ContosoPizza プロジェクト フォルダーがスコープになっているターミナルで、次のコマンドを実行して、データベース テーブルを作成するための移行を生成します。
dotnet ef migrations add InitialCreate --context PizzaContext
上記のコマンドでは次のことが行われます。
- 移行の名前は InitialCreate です。
--context
オプションでは、DbContext
から派生する、ContosoPizza プロジェクト内のクラスの名前が指定されます。
新しい Migrations ディレクトリが ContosoPizza プロジェクト ルートに表示されます。 ディレクトリには、データ定義言語 (DDL) 変更スクリプトに変換されるデータベースの変更を記述した <timestamp>_InitialCreate.cs ファイルが含まれます。
以下のコマンドを実行して、InitialCreate 移行を適用します。
dotnet ef database update --context PizzaContext
このコマンドによって移行が適用されます。 ContosoPizza.db は存在しないので、このコマンドによってプロジェクト ディレクトリに移行が作成されます。
ヒント
dotnet ef
ツールはすべてのプラットフォーム上でサポートされています。 Windows 上の Visual Studio では、統合された [パッケージ マネージャー コンソール] ウィンドウでAdd-Migration
とUpdate-Database
の PowerShell コマンドレットを使用できます。
データベースを検査する
EF Core によってアプリ用のデータベースが作成されました。 次に、SQLite 拡張機能を使用してデータベース内を見てみましょう。
[エクスプローラー] ペインで、ContosoPizza.db ファイルを右クリックし、[データベースを開く] を選択します。
SQLite Explorer フォルダーが [エクスプローラー] ペインに表示されます。
SQLite Explorer フォルダーを選択して、ノードとそのすべての子ノードを展開します。 ContosoPizza.db を右クリックし、[テーブル 'sqlite_master' の表示] を選択して、移行によって作成された完全なデータベース スキーマと制約を表示します。
- 各エンティティに対応するテーブルが作成されました。
- テーブル名は、
PizzaContext
のDbSet
プロパティの名前から取得されています。 Id
という名前のプロパティは、自動インクリメントされる主キー フィールドと推論されています。- EF Core の主キーおよび外部キー制約の名前付け規則は、それぞれ
PK_<primary key property>
およびFK_<dependent entity>_<principal entity>_<foreign key property>
です。<dependent entity>
および<principal entity>
プレースホルダーは、エンティティ クラスの名前に対応します。
Note
EF Core では、ASP.NET Core MVC と同様に、"構成よりも規則" というアプローチが使用されます。 EF Core 規則では、開発者の意図を推測することで、開発時間が短縮されます。 たとえば、
Id
または<entity name>Id
という名前のプロパティは、EF コアによって、生成されたテーブルの主キーと推測されます。 名前付け規則を採用しないことにした場合は、プロパティに[Key]
属性の注釈を付けるか、DbContext
のOnModelCreating
メソッドでキーとして構成する必要があります。
モデルを変更してデータベース スキーマを更新する
Contoso Pizza のマネージャーから示されたいくつかの新しい要件のため、エンティティ モデルを変更する必要があります。 次の手順では、マッピング属性 ("データ注釈" とも呼ばれる) を使用してモデルを変更します。
Models\Pizza.cs で、次の変更を行います。
System.ComponentModel.DataAnnotations
のusing
ディレクティブを追加します。Name
プロパティの前に[Required]
属性を追加し、プロパティを必須としてマークします。Name
プロパティの前に[MaxLength(100)]
属性を追加し、文字列の最大長を 100 に指定します。
更新後の Pizza.cs ファイルは次のコードのようになります。
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Pizza { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public Sauce? Sauce { get; set; } public ICollection<Topping>? Toppings { get; set; } }
Models\Sauce.cs で、次の変更を行います。
System.ComponentModel.DataAnnotations
のusing
ディレクティブを追加します。Name
プロパティの前に[Required]
属性を追加し、プロパティを必須としてマークします。Name
プロパティの前に[MaxLength(100)]
属性を追加し、文字列の最大長を 100 に指定します。IsVegan
という名前のbool
プロパティを追加します。
更新後の Sauce.cs ファイルは次のコードのようになります。
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Sauce { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public bool IsVegan { get; set; } }
Models\Topping.cs で、次の変更を行います。
System.ComponentModel.DataAnnotations
とSystem.Text.Json.Serialization
のusing
ディレクティブを追加します。Name
プロパティの前に[Required]
属性を追加し、プロパティを必須としてマークします。Name
プロパティの前に[MaxLength(100)]
属性を追加し、文字列の最大長を 100 に指定します。Name
プロパティの直後に、Calories
という名前のdecimal
プロパティを追加します。ICollection<Pizza>?
型のPizzas
プロパティを追加します。 この変更により、Pizza
-Topping
は多対多リレーションシップになります。[JsonIgnore]
属性をPizzas
プロパティに追加します。重要
この属性を使用すると、Web API のコードで応答を JSON にシリアル化するとき、
Topping
エンティティにPizzas
プロパティが含まれません。 この変更を行わないと、シリアル化されたトッピングのコレクションに、そのトッピングを使用するすべてのピザのコレクションが含まれます。 "その" コレクションの各ピザにはトッピングのコレクションが含まれ、そのそれぞれにピザのコレクションが再び含まれます。 この種の無限ループは "循環参照" と呼ばれ、シリアル化できません。
更新後の Topping.cs ファイルは次のコードのようになります。
using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace ContosoPizza.Models; public class Topping { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public decimal Calories { get; set; } [JsonIgnore] public ICollection<Pizza>? Pizzas { get; set; } }
すべての変更を保存して、
dotnet build
を実行します。次のコマンドを実行し、データベース テーブルを作成するための移行を生成します。
dotnet ef migrations add ModelRevisions --context PizzaContext
このコマンドは、ModelRevisions という名前の移行を作成します。
Note
次のメッセージが表示されます。データが失われる可能性がある操作がスキャフォールディングされました。移行の精度を確認してください。 このメッセージが表示されるのは、リレーションシップを
Pizza
からTopping
(一対多から多対多) に変更したため、既存の外部キー列を削除する必要があるからです。 データベースにはデータがまだないため、この変更は問題になりません。 しかし、一般に、この警告が表示されたときに生成された移行を調べ、移行によってデータが削除または切り捨てられていないことを確認することをお勧めします。以下のコマンドを実行して、ModelRevisions 移行を適用します。
dotnet ef database update --context PizzaContext
SQLite Explorer フォルダーのタイトル バーで、[データベースの更新] ボタンを選択します。
SQLite Explorer フォルダーで、ContosoPizza.db を右クリックします。 [Show Table 'sqlite_master'](テーブル 'sqlite_master' を表示) を選んで、完全なデータベース スキーマと制約を表示します。
重要
SQLite 拡張機能では、開いている SQLite のタブが再利用されます。
- ピザとトッピングの間の多対多リレーションシップを表すために、
PizzaTopping
結合テーブルが作成されています。 Toppings
とSauces
に新しいフィールドが追加されました。Calories
はtext
列として定義されています。これは SQLite に一致するdecimal
型がないためです。- 同様に、
IsVegan
はinteger
列として定義されています。 SQLite ではbool
型が定義されていません。 - どちらの場合も、変換は EF Core によって管理されます。
- 各テーブルの
Name
列はnot null
とマークされましたが、SQLite にはMaxLength
制約はありません。
ヒント
EF Core データベース プロバイダーは、モデル スキーマを特定のデータベースの機能にマップします。 SQLite には
MaxLength
に対応する制約は実装されていませんが、SQL Server や PostgreSQL などの他のデータベースには実装されています。- ピザとトッピングの間の多対多リレーションシップを表すために、
SQLite Explorer フォルダーで、
_EFMigrationsHistory
テーブルを右クリックして、[テーブルの表示] を選択します。 このテーブルには、データベースに適用されたすべての移行の一覧が含まれます。 2 つの移行を実行したので、2 つのエントリがあります。1 つは InitialCreate 移行用、もう 1 つは ModelRevisions 用です。
Note
この演習ではマッピング属性 (データ注釈) を使用して、モデルをデータベースにマップしました。 マッピング属性の代わりに、ModelBuilder fluent API を使用してモデルを構成できます。 どちらの方法も有効ですが、一部の開発者はどちらか一方のアプローチを優先します。
移行を使用して、データベース スキーマを定義および更新しました。 次のユニットでは、データを操作する PizzaService
のメソッドを完成させます。