次の方法で共有


Blazor ムービー データベース アプリを構築する (パート 4 - データベースを操作する)

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

この記事は、映画データベースを管理する機能を備えた ASP.NET Core Blazor の構築の基本について説明する、Blazor Web App 映画データベース アプリ チュートリアルの第 4 部です。

チュートリアル シリーズのこの部分では、データベース コンテキストに焦点を当て、データベースのスキーマとデータを直接操作します。 データを使用したデータベースのシード処理についても説明します。

運用アプリに必要な安全な認証フロー

このチュートリアルでは、ユーザー認証が不要なローカル データベースを使用します。 運用アプリでは、使用可能な最も安全な認証フローを使用する必要があります。 デプロイされたテストおよび運用 Blazor Web App の認証の詳細については、次のリソースをご覧ください。

Microsoft Azure サービスの場合は、マネージド ID を使用することをお勧めします。 マネージド ID を使用すると、アプリ コードに資格情報を保存せずに、Azure サービスに対して安全に認証を行うことができます。 詳細については、次のリソースを参照してください。

データベース コンテキスト

データベース コンテキストである BlazorWebAppMoviesContext は、データベースに接続し、モデル オブジェクトをデータベース レコードにマップします。 データベース コンテキストは、このシリーズの第 2 部で作成されました。 スキャフォールディングされたデータベース コンテキスト コードは、 Program ファイルに表示されます。

builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ?? 
        throw new InvalidOperationException(
            "Connection string 'BlazorWebAppMoviesContext' not found.")));
builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
    options.UseSqlite(
        builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ?? 
        throw new InvalidOperationException(
            "Connection string 'BlazorWebAppMoviesContext' not found.")));
builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
    options.UseSqlite(
        builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ?? 
        throw new InvalidOperationException(
            "Connection string 'BlazorWebAppMoviesContext' not found.")));

AddDbContextFactory は、特定のコンテキストのファクトリをアプリのサービス コレクション内のサービスとして登録します。

UseSqlServer または UseSqlite は、Microsoft SQL Server または SQLite データベースに接続するようにコンテキストを構成します。 その他のプロバイダーは、追加の種類のデータベースに接続できます。

GetConnectionString は、ASP.NET Core 構成システムを使用して、指定された接続文字列名の ConnectionStrings キーを読み取ります。これは、前の例では BlazorWebAppMoviesContext です。

ローカルで開発する場合は、構成によってアプリ設定ファイル (appsettings.json) からデータベース接続文字列が取得されます。 次の例の {CONNECTION STRING} プレースホルダーは接続文字列です。

"ConnectionStrings": {
  "BlazorWebAppMoviesContext": "{CONNECTION STRING}"
}

接続文字列の例を次に示します。

Server=(localdb)\mssqllocaldb;Database=BlazorWebAppMoviesContext-00001111-aaaa-2222-bbbb-3333cccc4444;Trusted_Connection=True;MultipleActiveResultSets=true

アプリがテスト/ステージング サーバーまたは運用サーバーに展開されている場合は、プロジェクトの構成ファイルの外部に接続文字列を安全に格納します。

警告

アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。

データベース テクノロジ

このチュートリアルの Visual Studio バージョンでは、SQL Server を使用します。

SQL Server Express LocalDB は、プログラム開発を対象にした、SQL Server Express データベース エンジンの軽量バージョンです。 LocalDB は要求時に開始され、ユーザー モードで実行されるため、複雑な構成はありません。 マスター データベース ファイル (*.mdf) は C:/Users/{USER} ディレクトリに配置されます。ここでは、{USER} プレースホルダーはシステムのユーザー ID です。

[表示] メニューの [SQL Server オブジェクト エクスプローラー] (SSOX) を開きます。

[View] メニュー

Movie テーブルを右クリックし、Movie を選択します。

ソリューション エクスプローラー (SSOX) で Movie テーブルを開くコンテキスト メニュー

ビュー デザイナーが開きます。

テーブル デザイナーで開いている Movie テーブル

ID の横のキー アイコンに注意してください。 EF で主キーに ID という名前のプロパティが作成されます。

Movie テーブルを右クリックし、Movie を選択します。

ソリューション エクスプローラー (SSOX) で Movie テーブル データを開くコンテキスト メニュー

テーブルのデータが Visual Studio の新しいタブで開きます。

Movie テーブル データを表示している開いた Movie テーブル

このチュートリアルの VS Code バージョンでは、SQLite を使用します。これは、パブリックで自己完結型のフル機能の SQL データベース エンジンです。

SQLite データベースを管理および表示するために使用できるサードパーティ製ツールは多数あります。 次の画像は、DB Browser for SQLite を示しています。

Movie データベースを示している DB Browser for SQLite

このチュートリアルでは、EF Core 移行を使用します。 移行では、データ モデルの変更に合わせてデータベース スキーマが更新されます。 ただし、移行は、 EF Core プロバイダーがサポートするデータベースにのみ変更を加えることができます。 詳細については、この記事の最後にリソースを示します。

このチュートリアルの VS Code バージョンでは、SQLite を使用します。これは、パブリックで自己完結型のフル機能の SQL データベース エンジンです。

SQLite データベースを管理および表示するために使用できるサードパーティ製ツールは多数あります。 次の画像は、DB Browser for SQLite を示しています。

Movie データベースを示している DB Browser for SQLite

このチュートリアルでは、EF Core 移行を使用します。 移行では、データ モデルの変更に合わせてデータベース スキーマが更新されます。 ただし、移行は、 EF Core プロバイダーがサポートするデータベースにのみ変更を加えることができます。 詳細については、この記事の最後にリソースを示します。

データベースのシード

シード処理コードは、開発テスト用のレコードのセットを作成したり、新しい運用データベースの初期データを作成するために使用したりできます。

SeedData フォルダーに、次のコードで Data という名前の新しいクラスを作成します。

Data/SeedData.cs:

using Microsoft.EntityFrameworkCore;
using BlazorWebAppMovies.Models;

namespace BlazorWebAppMovies.Data;

public class SeedData
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        using var context = new BlazorWebAppMoviesContext(
            serviceProvider.GetRequiredService<
                DbContextOptions<BlazorWebAppMoviesContext>>());

        if (context == null || context.Movie == null)
        {
            throw new NullReferenceException(
                "Null BlazorWebAppMoviesContext or Movie DbSet");
        }

        if (context.Movie.Any())
        {
            return;
        }

        context.Movie.AddRange(
            new Movie
            {
                Title = "Mad Max",
                ReleaseDate = new DateOnly(1979, 4, 12),
                Genre = "Sci-fi (Cyberpunk)",
                Price = 2.51M,
            },
            new Movie
            {
                Title = "The Road Warrior",
                ReleaseDate = new DateOnly(1981, 12, 24),
                Genre = "Sci-fi (Cyberpunk)",
                Price = 2.78M,
            },
            new Movie
            {
                Title = "Mad Max: Beyond Thunderdome",
                ReleaseDate = new DateOnly(1985, 7, 10),
                Genre = "Sci-fi (Cyberpunk)",
                Price = 3.55M,
            },
            new Movie
            {
                Title = "Mad Max: Fury Road",
                ReleaseDate = new DateOnly(2015, 5, 15),
                Genre = "Sci-fi (Cyberpunk)",
                Price = 8.43M,
            },
            new Movie
            {
                Title = "Furiosa: A Mad Max Saga",
                ReleaseDate = new DateOnly(2024, 5, 24),
                Genre = "Sci-fi (Cyberpunk)",
                Price = 13.49M,
            });

        context.SaveChanges();
    }
}

依存関係挿入 (DI) コンテナーからデータベース コンテキスト インスタンスを取得します。 ムービーが存在する場合は、データベースのシード処理を回避するために return が呼び出されます。 データベースが空の場合、Mad Max シリーズWarner Bros. Entertainment) ムービーがシードされます。

シード初期化子を実行するには、アプリ (Program) をビルドする行の直後に var app = builder.Build(); ファイルに次のコードを追加します。 using ステートメントでは、シード処理の完了後にデータベース コンテキストが破棄されます。

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    SeedData.Initialize(services);
}

データベースに以前のテストのレコードが含まれている場合は、アプリを実行し、データベースで作成したエンティティを削除します。 ブラウザーのウィンドウを閉じて、アプリを停止します。

データベースに以前のテストのレコードが含まれている場合は、アプリを実行し、データベースで作成したエンティティを削除します。 ブラウザーのウィンドウを閉じ、VS Code でキーボードの Shift+F5 キーを押して、アプリを停止します。

データベースに以前のテストのレコードが含まれている場合は、アプリを実行し、データベースで作成したエンティティを削除します。 ブラウザーのウィンドウを閉じ、コマンド シェルで Ctrl +C (Windows)押して、アプリを停止します。

データベースが空の場合は、アプリを実行します。

ムービー Index ページに移動して、シード処理されたムービーを表示します。

データベースをシード処理した後の Mad Max ムービー リストを示すムービー インデックス ページ

フォームをモデルにバインドする

Edit コンポーネント (Components/Pages/MoviePages/Edit.razor) を確認します。

(たとえば、相対 URL: Edit で) /movies/edit?id=6 コンポーネント ページに対して HTTP GET 要求が行われた場合

  • OnInitializedAsync メソッドは、データベースから Id6 を使用してムービーを取ってきて、Movie プロパティに割り当てます。
  • EditForm.Model パラメータは、フォームの最上位モデル オブジェクトを指定します。 割り当てられたモデルを使用して、フォームの編集コンテキストが構築されます。
  • フォームには、ムービーからの値が表示されます。

Edit ページがサーバーにポストされると、Movie[SupplyParameterFromForm] プロパティで注釈が付いているため、ページのフォーム値は プロパティにバインドされます。

[SupplyParameterFromForm]
private Movie? Movie { get; set; }

フォームがポストされるときにモデルの状態にエラーがある場合 (たとえば、ReleaseDate を日付に変換できない場合)、送信された値を含むフォームが再表示されます。 モデルのエラーが存在しない場合、ムービーはフォームのポストされた値を使用して保存されます。

コンカレンシーの例外処理

UpdateMovie コンポーネント (Edit) の Components/Pages/MoviePages/Edit.razor メソッドを確認します。

private async Task UpdateMovie()
{
    using var context = DbFactory.CreateDbContext();
    context.Attach(Movie!).State = EntityState.Modified;

    try
    {
        await context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!MovieExists(Movie!.Id))
        {
            NavigationManager.NavigateTo("notfound");
        }
        else
        {
            throw;
        }
    }

    NavigationManager.NavigateTo("/movies");
}

1 人のクライアントがムービーを削除し、別のクライアントがムービーに変更を投稿した場合に、コンカレンシーの例外を検出します。

前のコードによるコンカレンシーの処理方法をテストするには:

  1. ムービー用に Edit を選択し、変更を行います。ただし、Save は選択しないでください。
  2. 別のブラウザー ウィンドウで、ムービー Index ページにアプリを開き、同じムービーの Delete リンクを選択してムービーを削除します。
  3. 前のブラウザー ウィンドウで、Save ボタンを選択してムービーに変更をポストします。
  4. ブラウザーが notfound エンドポイントに移動します。このエンドポイントは存在せず、404 (Not Found) の結果になります。

EF Core アプリでの Blazor のコンカレンシーの処理に関するその他のガイダンスについては、Blazor ドキュメントをご覧ください。

アプリを停止する

アプリが実行されている場合は、ブラウザーのウィンドウを閉じてアプリをシャットダウンします。

アプリが実行されている場合は、ブラウザーのウィンドウを閉じ、VS Code でキーボードの Shift+F5 キーを押して、アプリをシャットダウンします。

アプリが実行されている場合は、ブラウザーのウィンドウを閉じ、コマンド シェルで Ctrl +C押して、アプリをシャットダウンします。

完成したサンプルを使用したトラブルシューティング

チュートリアルの実行中にテキストから解決できない問題が発生した場合は、コードを、 Blazor サンプル リポジトリの完成したプロジェクトと比較します。

Blazor サンプル GitHub リポジトリ (dotnet/blazor-samples)

最新バージョンのフォルダーを選択します。 このチュートリアルのプロジェクトのサンプル フォルダーには、BlazorWebAppMovies という名前が付けられています。

その他のリソース

Mad MaxThe Road WarriorMad Max: Beyond ThunderdomeMad Max: Fury RoadFuriosa: A Mad Max Saga は、Warner Bros. Entertainment の商標および著作物です。

次のステップ