演習 - 移行を設定する

完了

このユニットでは、ローカル環境の SQLite データベースのテーブルにマップされる、C# のエンティティ クラスを作成します。 それらのエンティティから、EF Core 移行機能でテーブルを生成します。

移行によって、データベース スキーマを増分更新する方法が提供されます。

プロジェクト ファイルを取得する

作業を開始するには、プロジェクト ファイルを取得します。 プロジェクト ファイルを取得する方法には、いくつかのオプションがあります。

  • GitHub Codespaces を使用する
  • GitHub リポジトリをクローンする

互換性のあるコンテナー ランタイムがインストールされている場合は、Dev Containers 拡張機能を使って、ツールがプレインストールされたコンテナーでリポジトリを開くこともできます。

GitHub Codespaces を使用する

codespace は、クラウドでホストされている IDE です。 GitHub Codespaces を使用している場合は、ブラウザーでリポジトリに移動します[コード] を選択し、main ブランチに新しい codespace を作成します。

GitHub リポジトリをクローンする

GitHub Codespaces を使用していない場合は、プロジェクトの GitHub リポジトリをクローンし、ファイルを Visual Studio Code でフォルダーとして開くことができます。

  1. コマンド ターミナルを開き、コマンド プロンプトを使用して GitHub からプロジェクトをクローンします。

    git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
    
  2. 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.csApiController の値であり、HTTP POST、GET、PUT、DELETE 動詞用のエンドポイントを公開しています。 これらの動詞によって、PizzaService 上の対応する CRUD メソッドが呼び出されます。 PizzaService は、PizzaController コンストラクターに挿入されます。
  • Models フォルダー内には、PizzaServicePizzaController によって使用されるモデルがあります。
  • エンティティ モデル Pizza.csTopping.csSauce.cs の間には、次のようなリレーションシップがあります。
    • 1 つのピザで、1 つ以上のトッピングを使用できます。
    • 1 つのトッピングを、1 つ以上のピザで使用できます。
    • 1 つのピザで使用できるソースは 1 種類ですが、1 種類のソースを多くのピザで使用できます。

アプリのビルド

Visual Studio Code でアプリをビルドするには:

  1. [エクスプローラー] ペインで ContosoPizza ディレクトリを右クリックし、[統合ターミナルで開く] を選択します。

    ContosoPizza ディレクトリをスコープとするターミナル ペインが開きます。

  2. 次のコマンドを使用してアプリをビルドします。

    dotnet build
    

    コードは、警告やエラーなしでビルドされるはずです。

NuGet パッケージと EF Core ツールを追加する

このモジュールで使用するデータベース エンジンは SQLite です。 SQLite は、軽量のファイルベースのデータベース エンジンです。 これは開発とテストに適しており、小規模な運用環境のデプロイにも適しています。

注意

前述のように、EF Core のデータベース プロバイダーはプラグ可能です。 SQLite は軽量でクロスプラットフォームであるため、このモジュールに適しています。 同じコードを使用して、SQL Server や PostgreSQL などのさまざまなデータベース エンジンを操作できます。 同じアプリで複数のデータベース エンジンを使用することもできます。

始める前に、必要なパッケージを追加します。

  1. ターミナル ペインで、次のコマンドを実行します。

    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
    

    このコマンドでは、EF Core SQLite データベース プロバイダーとそのすべての依存関係 (一般的な EF Core サービスなど) を含む NuGet パッケージを追加します。

  2. 次のコマンドを実行します。

    dotnet add package Microsoft.EntityFrameworkCore.Design
    

    このコマンドを実行すると、EF Core ツールに必要なパッケージが追加されます。

  3. 完了するには、次のコマンドを実行します。

    dotnet tool install --global dotnet-ef
    

    このコマンドを実行すると、移行とスキャフォールディングの作成に使用する dotnet ef ツールがインストールされます。

    ヒント

    dotnet ef が既にインストールされている場合は、dotnet tool update --global dotnet-ef を実行して更新できます。

モデルと DbContext をスキャフォールディングする

次に、DbContext の実装を追加して構成します。 DbContext は、データベースとの対話を可能にするゲートウェイです。

  1. ContosoPizza ディレクトリを右クリックし、Data という新しいフォルダーを追加します。

  2. 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 をインスタンス化すると、PizzasToppingsSauces の各プロパティが公開されます。 それらのプロパティによって公開されるコレクションに対して行った変更は、データベースに反映されます。
  3. Program.cs で、// Add the PizzaContext を次のコードに置き換えます。

    builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
    

    上記のコードでは次の操作が行われます。

    • PizzaContext を、ASP.NET Core 依存関係挿入システムに登録します。
    • PizzaContext で SQLite データベース プロバイダーを使用することを指定します。
    • ローカル ファイル ContosoPizza.db を指し示す SQLite 接続文字列を定義します。

    注意

    SQLite はローカル データベース ファイルを使用するため、接続文字列をハードコーディングしてもおそらく問題ありません。 PostgreSQL や SQL Server のようなネットワーク データベースの場合は、常に接続文字列を安全に格納する必要があります。 ローカル開発の場合は、Secret Manager を使用します。 運用環境のデプロイでは、Azure Key Vault のようなサービスの使用を検討します。

  4. また、Program.cs で、// Additional using declarations を次のコードに置き換えます。

    using ContosoPizza.Data;
    

    このコードは、前のステップの依存関係を解決します。

  5. すべての変更を保存します。 GitHub Codespaces によって、変更が自動的に保存されます。

  6. dotnet build を実行して、ターミナルでアプリをビルドします。 ビルドは警告やエラーなしで成功するはずです。

移行を作成して実行する

次に、初期データベースの作成に使用できる移行を作成します。

  1. 次のコマンドを実行し、データベース テーブルを作成するための移行を生成します。

    dotnet ef migrations add InitialCreate --context PizzaContext
    

    上記のコマンドでは次のことが行われます。

    • 移行に InitialCreate という名前が付けられます。
    • --context オプションでは、DbContext から派生する、ContosoPizza プロジェクト内のクラスの名前が指定されます。

    新しい Migrations ディレクトリが ContosoPizza プロジェクト ルートに表示されます。 ディレクトリには、データ定義言語 (DDL) 変更スクリプトに変換されるデータベースの変更を記述した <timestamp>_InitialCreate.cs ファイルが含まれます。

  2. 以下のコマンドを実行して、InitialCreate 移行を適用します。

    dotnet ef database update --context PizzaContext
    

    このコマンドによって移行が適用されます。 ContosoPizza.db は存在しないので、移行はプロジェクト ディレクトリに作成されます。

    ヒント

    dotnet ef ツールはすべてのプラットフォーム上でサポートされています。 Windows 上の Visual Studio では、統合された [パッケージ マネージャー コンソール] ウィンドウで Add-MigrationUpdate-Database の PowerShell コマンドレットを使用できます。

データベースを検査する

EF Core によってアプリ用のデータベースが作成されました。 次に、SQLite 拡張機能を使用してデータベース内を見てみます。

  1. [エクスプローラー] ペインで、ContosoPizza.db ファイルを右クリックし、[データベースを開く] を選択します。

    Visual Studio Code の [エクスプローラー] ペインの [データベースを開く] メニュー オプションを示すスクリーンショット。

    SQLite Explorer フォルダーが [エクスプローラー] ペインに表示されます。

    [エクスプローラー] ペインの SQLite Explorer フォルダーを示すスクリーンショット。

  2. SQLite Explorer フォルダーを選択して、ノードとそのすべての子ノードを展開します。 ContosoPizza.db を右クリックし、[テーブル 'sqlite_master' の表示] を選択して、移行によって作成された完全なデータベース スキーマと制約を表示します。

    [エクスプローラー] ペインで展開された SQLite Explorer フォルダーを示すスクリーンショット。

    • 各エンティティに対応するテーブルが作成されました。
    • テーブル名は、PizzaContextDbSet プロパティの名前から取得されています。
    • 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 という名前のプロパティは、生成されたテーブルの主キーと推測されます。 名前付け規則を採用しないことにした場合は、プロパティに [Key] 属性の注釈を付けるか、DbContextOnModelCreating メソッドでキーとして構成する必要があります。

モデルを変更してデータベース スキーマを更新する

Contoso Pizza のマネージャーから示されたいくつかの新しい要件のため、エンティティ モデルを変更する必要があります。 次の手順では、マッピング属性 ("データ注釈" とも呼ばれる) を使用してモデルを変更します。

  1. Models\Pizza.cs で、次の変更を行います。

    1. System.ComponentModel.DataAnnotationsusing ディレクティブを追加します。
    2. Name プロパティの前に [Required] 属性を追加し、プロパティを必須としてマークします。
    3. Name プロパティの前に [MaxLength(100)] 属性を追加し、文字列の最大長を 100 に指定します。
    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; }
    }
    
  2. Models\Sauce.cs で、次の変更を行います。

    1. System.ComponentModel.DataAnnotationsusing ディレクティブを追加します。
    2. Name プロパティの前に [Required] 属性を追加し、プロパティを必須としてマークします。
    3. Name プロパティの前に [MaxLength(100)] 属性を追加し、文字列の最大長を 100 に指定します。
    4. IsVegan という名前の bool プロパティを追加します。
    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; }
    }
    
  3. Models\Topping.cs で、次の変更を行います。

    1. System.ComponentModel.DataAnnotationsSystem.Text.Json.Serializationusing ディレクティブを追加します。
    2. Name プロパティの前に [Required] 属性を追加し、プロパティを必須としてマークします。
    3. Name プロパティの前に [MaxLength(100)] 属性を追加し、文字列の最大長を 100 に指定します。
    4. Name プロパティの直後に、Calories という名前の decimal プロパティを追加します。
    5. ICollection<Pizza>? 型の Pizzas プロパティを追加して、Pizza-Topping (多対多) リレーションシップを作成します。
    6. [JsonIgnore] 属性を Pizzas プロパティに追加します。

    重要

    これらの手順により、Web API のコードで応答を JSON にシリアル化するとき、Topping エンティティに Pizzas プロパティが含まれなくなります。 この変更を行わないと、シリアル化されたトッピングのコレクションに、そのトッピングを使用するすべてのピザのコレクションが含まれます。 "その" コレクションの各ピザにはトッピングのコレクションが含まれ、そのそれぞれにピザのコレクションが再び含まれます。 この種の無限ループは "循環参照" と呼ばれ、シリアル化できません。

    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; }
    }
    
  4. すべての変更を保存して、dotnet build を実行します。

  5. 次のコマンドを実行し、データベース テーブルを作成するための移行を生成します。

    dotnet ef migrations add ModelRevisions --context PizzaContext
    

    ModelRevisions という名前の移行が作成されます。

    注意

    このメッセージが表示されます: "データが失われる可能性がある操作がスキャフォールディングされました。移行の精度を確認してください"。 このメッセージが表示されるのは、リレーションシップを Pizza から Topping (一対多から多対多) に変更したため、既存の外部キー列を削除する必要があるからです。 データベースにはデータがまだないため、この変更は問題になりません。 しかし、一般に、この警告が表示されたときに生じた移行を調べ、移行によってデータが削除または切り捨てられていないことを確認することをお勧めします。

  6. 以下のコマンドを実行して、ModelRevisions 移行を適用します。

    dotnet ef database update --context PizzaContext
    
  7. SQLite Explorer フォルダーのタイトル バーで、[データベースの更新] ボタンを選択します。

    SQLite Explorer のタイトル バーにある [データベースの更新] ボタンを示すスクリーンショット。

  8. SQLite Explorer フォルダーで、ContosoPizza.db を右クリックします。 [Show Table 'sqlite_master'](テーブル 'sqlite_master' を表示) を選んで、完全なデータベース スキーマと制約を表示します。

    重要

    SQLite 拡張機能では、開いている SQLite のタブが再利用されます。

    • ピザとトッピングの間の多対多リレーションシップを表すために、PizzaTopping 結合テーブルが作成されています。
    • ToppingsSauces に新しいフィールドが追加されています。
      • Caloriestext 列として定義されています。これは SQLite に一致する decimal 型がないためです。
      • 同様に、IsVeganinteger 列として定義されています。 SQLite では bool 型が定義されていません。
      • どちらの場合も、変換は EF Core によって管理されます。
    • 各テーブルの Name 列は not null とマークされていますが、SQLite には MaxLength 制約はありません。

    ヒント

    EF Core データベース プロバイダーは、モデル スキーマを特定のデータベースの機能にマップします。 SQLite には MaxLength に対応する制約は実装されていませんが、SQL Server や PostgreSQL などの他のデータベースには実装されています。

  9. SQLite Explorer フォルダーで、_EFMigrationsHistory テーブルを右クリックして、[テーブルの表示] を選択します。 このテーブルには、データベースに適用されたすべての移行の一覧が含まれます。 2 つの移行を実行したので、2 つのエントリがあります。1 つは InitialCreate 移行用、もう 1 つは ModelRevisions 用です。

注意

この演習ではマッピング属性 (データ注釈) を使用して、モデルをデータベースにマップしました。 マッピング属性の代わりに、ModelBuilder fluent API を使用してモデルを構成できます。 どちらの方法も有効ですが、一部の開発者はどちらか一方のアプローチを優先します。

移行を使用して、データベース スキーマを定義および更新しました。 次のユニットでは、データを操作する PizzaService のメソッドを完成させます。

自分の知識をチェックする

1.

エンティティ クラスにおける、主キーのプロパティの名前付け規則は何ですか?