注
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事は、映画データベースを管理する機能を備えた ASP.NET Core Blazor の構築の基本について説明する、Blazor Web App 映画データベース アプリ チュートリアルの第 3 部です。
このチュートリアル シリーズのこのパートでは、アプリにスキャフォールディングされたプロジェクト内の Razor コンポーネントを調べます。 映画データの表示が改善されました。
Razor のコンポーネント
Blazor アプリは "Razor コンポーネント" が基になっており、単に "コンポーネント" と呼ばれることがよくあります。 "コンポーネント" とは、ページ、ダイアログ、データ入力フォームといった UI の要素です。 コンポーネントは、.NET アセンブリに組み込まれている .NET C# クラスです。
Razor は、クライアント側の UI ロジックと構成用に Razor マークアップ ページの形式 (ファイル拡張子 .razor
) でコンポーネントが通常記述される方法を示しています。 Razor とは、開発者の生産性のために設計された、C# コードに HTML マークアップを結合するための構文です。
"Blazor コンポーネント" という用語を使う開発者やオンライン リソースもありますが、このドキュメントではその用語は使わず、正式名の "Razor コンポーネント" (または、単に "コンポーネント") を使っています。
Razor コンポーネントの構造には、次の一般的なパターンがあります。
- コンポーネント定義 (
.razor
ファイル) の先頭にあるさまざまな Razor ディレクティブは、コンポーネント マークアップのコンパイル方法または関数を指定します。 - 次に、Razor マークアップは、通常の HTML 要素を含む HTML のレンダリング方法を指定します。
- 最後に、
@code
ブロックには、コンポーネント パラメーターやイベント ハンドラーなど、コンポーネント クラスのメンバーを定義する C# コードが含まれています。
次の Welcome
コンポーネント (Welcome.razor
) について考えてみましょう。
@page "/welcome"
<PageTitle>Welcome!</PageTitle>
<h1>Welcome to Blazor!</h1>
<p>@welcomeMessage</p>
@code {
private string welcomeMessage = "We ❤️ Blazor!";
}
最初の行は、Razor コンポーネント内の重要なRazorRazor コンストラクトを表します。 Razor ディレクティブは、コンポーネント マークアップのコンパイル方法または関数を変更する @
マークアップに表示される Razor の接頭辞が付いた予約キーワードです。 @page
Razor ディレクティブは、コンポーネントのルート テンプレートを指定します。 このコンポーネントには、ブラウザーで相対 URL /welcome
を使用してアクセスします。 慣例により、コンポーネント定義ファイルの先頭には、ほとんどのコンポーネントのディレクティブが配置されます。
PageTitle コンポーネントは、ページ タイトルを指定するフレームワークに組み込まれているコンポーネントです。
"Welcome to Blazor!
" は、H1 見出し要素 (<h1>
) のコンテンツごとに、コンポーネントの最初にレンダリングされた本文のマークアップです。
次に、C# 変数 (Razor) の前にアットマーク (@
) を付けることで、welcomeMessage
構文を使用してウェルカム メッセージが表示されます。
@code
ブロックには、コンポーネントの C# コードが含まれます。 welcomeMessage
は、値で初期化されたプライベート文字列です。
この記事の次のセクションでは、次を説明します。
- Web ページのナビゲーションとレイアウトの 3 つのコンポーネントである、
NavMenu
、NavLink、MainLayout
の各コンポーネントについて説明します。 - 映画データベース エンティティに対する CRUD 操作用にスキャフォールディングによって作成されるコンポーネントについて説明します。
ナビゲーション用の NavMenu
コンポーネント
NavMenu
コンポーネント (Components/Layout/NavMenu.razor
) は、他の NavLink コンポーネントへのナビゲーション リンクをレンダリングする Razor コンポーネントを使用して、サイドバー ナビゲーションを実装します。
NavLink コンポーネントは <a>
要素のように動作しますが、active
が現在の URL と一致するかどうかに基づいて href
CSS クラスを切り替える点が異なります。 active
クラスは、表示されているナビゲーション リンクの中でどのページがアクティブ ページであるかをユーザーが理解するのに役立ちます。 NavLinkMatch.All が Match パラメーターに割り当てられると、アクティブな CSS クラスが現在の URL 全体と一致したときに表示されるようにコンポーネントが構成されます。
NavLink コンポーネントは、Blazor アプリで使用するBlazor フレームワークに組み込まれていますが、NavMenu
コンポーネントはプロジェクト テンプレートBlazor一部にすぎません。
Components/Layout/NavMenu.razor
:
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">BlazorWebAppMovies</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
</nav>
</div>
NavMenu
コンポーネントの最初の <div>
要素のブランド リンク テキスト (<a>
要素コンテンツ) に注目してください。 ブランドを BlazorWebAppMovies
から Sci-fi Movies
に変更します。
- <a class="navbar-brand" href="">BlazorWebAppMovies</a>
+ <a class="navbar-brand" href="">Sci-fi Movies</a>
ユーザーが映画 Index
ページにアクセスできるようにするには、ナビゲーション メニュー エントリを NavMenu
コンポーネントに追加します。 <div>
コンポーネントの Weather
のマークアップ (NavLink
) の直後に、次のマークアップを追加します。
<div class="nav-item px-3">
<NavLink class="nav-link" href="movies">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Movies
</NavLink>
</div>
上記の変更を行った後の最後の NavMenu
コンポーネント:
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">Sci-fi Movies</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="nav flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="movies">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Movies
</NavLink>
</div>
</nav>
</div>
アプリを実行して、サイドバー ナビゲーションの上部にある更新されたブランドと、映画ページ (映画) にアクセスするためのリンクを確認します。
ブラウザーのウィンドウを閉じて、アプリを停止します。
ブラウザーのウィンドウを閉じ、VS Code でキーボードの Shift+F5 キーを押して、アプリを停止します。
ブラウザーのウィンドウを閉じ、コマンド シェルで Ctrl +押して、アプリを停止します。
レイアウト用の MainLayout
コンポーネント
MainLayout
コンポーネントはアプリの既定のレイアウトです。 MainLayout
コンポーネントは、レイアウトを表すコンポーネントの基底クラスである LayoutComponentBase を継承します。 レイアウトを使用するアプリのコンポーネントは、マークアップに Body (@Body
) が表示される場所にレンダリングされます。
Components/Layout/MainLayout.razor
:
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
MainLayout
コンポーネントには、次の追加仕様が採用されています。
NavMenu
コンポーネントがサイドバーにレンダリングされます。 コンポーネント名を含む HTML タグのみを配置して、コンポーネントを Razor マークアップ内のその場所にレンダリングする必要があることに注意してください。 これにより、コンポーネントを互いに入れ子にしたり、実装する HTML レイアウト内で入れ子にすることができます。<main>
要素のコンテンツには、次のものが含まれます。- ASP.NET Core ドキュメントのランディング ページにユーザーを移動させる About リンク。
<article>
(Body) パラメーターを持つ@Body
要素。レイアウトを使用するコンポーネントがレンダリングされます。- ハンドルされないエラーに関する通知が表示されるエラー UI (
<div id="blazor-error-ui" ...>
)。
MainLayout
コンポーネント (Routes
) では、既定のレイアウト (Components/Routes.razor
) を指定します。
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
個々のコンポーネントは、既定以外の独自のレイアウトを自由に設定でき、同じフォルダー内の _Imports.razor
ファイルを使用して、コンポーネントのフォルダー全体にレイアウトを適用できます。 これらの機能については、Blazor のドキュメントで詳しく説明しています。
作成、読み取り、更新、削除 (CRUD) の各コンポーネント
以降のセクションでは、映画 CRUD コンポーネントの構成とそのしくみについて説明します。
Index
コンポーネント
Index
コンポーネント定義ファイル (Components/Pages/Movies/Index.razor
) を開き、ファイルの先頭にある Razor ディレクティブを調べます。
@page
ディレクティブのルート テンプレートは、ページの URL が /movies
であることを示します。
次の API にアクセスする @using
ディレクティブが表示されます。
- Microsoft.EntityFrameworkCore
- Microsoft.AspNetCore.Components.QuickGrid
BlazorWebAppMovies.Models
BlazorWebAppMovies.Data
型 (IDbContextFactory<T>
) がT
であるデータベース コンテキスト ファクトリ (BlazorWebAppMoviesContext
) は、@inject
ディレクティブを使用してコンポーネントに挿入されます。 このファクトリ アプローチではデータベース コンテキストを破棄する必要があるため、コンポーネントに IAsyncDisposable ディレクティブを使用して @implements
インターフェイスを実装します。
ページ タイトルは、Blazor フレームワークの PageTitle コンポーネントを介して設定され、H1 セクションの見出しが最初にレンダリングされる要素です。
<PageTitle>Index</PageTitle>
<h1>Index</h1>
リンクがレンダリングされ、Create
の /movies/create
ページに移動します。
<p>
<a href="movies/create">Create New</a>
</p>
QuickGrid
コンポーネント には、ムービー エンティティが表示されます。 アイテムプロバイダーは、作成済みデータベース コンテキスト (DbSet<Movie>
) から取得された CreateDbContext であり、それは挿入されたデータベース コンテキスト ファクトリ (DbFactory
) に由来しています。 それぞれの映画エンティティについて、コンポーネントには映画のタイトル、リリース日、ジャンル、価格が表示されます。 列には、編集、詳細の表示、各映画エンティティの削除を行うリンクも保持されます。
<QuickGrid Class="table" Items="context.Movie">
<PropertyColumn Property="movie => movie.Title" />
<PropertyColumn Property="movie => movie.ReleaseDate" />
<PropertyColumn Property="movie => movie.Genre" />
<PropertyColumn Property="movie => movie.Price" />
<TemplateColumn Context="movie">
<a href="@($"movies/edit?id={movie.Id}")">Edit</a> |
<a href="@($"movies/details?id={movie.Id}")">Details</a> |
<a href="@($"movies/delete?id={movie.Id}")">Delete</a>
</TemplateColumn>
</QuickGrid>
@code {
private BlazorWebAppMoviesContext context = default!;
protected override void OnInitialized()
{
context = DbFactory.CreateDbContext();
}
public async ValueTask DisposeAsync() => await context.DisposeAsync();
}
このコード ブロック (@code
)では次の操作を行います。
context
フィールドはデータベース コンテキストを保持し、BlazorWebAppMoviesContext
として型指定されます。OnInitialized
ライフサイクル メソッドは、挿入されたファクトリ (CreateDbContext) から、作成されたデータベース コンテキスト (DbFactory
) をcontext
変数に割り当てます。- 非同期の
DisposeAsync
メソッドは、コンポーネントが破棄されるときにデータベース コンテキストを破棄します。
Context
のコンテキスト (TemplateColumn<TGridItem>) パラメーターが、列のコンテキスト インスタンスのパラメーター名 (movie
) を指定する方法に注目してください。 コンテキスト インスタンスの名前を指定すると、マークアップがさらに読みやすくなります (コンテキストの既定の名前は、単に context
)。 Movie
クラス プロパティは、コンテキスト インスタンスから読み取られます。 たとえば、映画の識別子 (Id
) は movie.Id
で使用できます。
@
と呼ばれる、かっこ付きアット (@(...)
) マーク (Razor) を使用すると、各リンクの では、映画エンティティの href
プロパティをリンク クエリ文字列にId
() として含めることができます。 映画の識別子 (Id
) が 7 の場合、その映画を編集するために href
に指定された文字列の値は movies/edit?id=7
です。 リンクに従うと、"id
" フィールドは、映画を読み込む Edit
コンポーネントによってクエリ文字列から読み取られます。
チュートリアル シリーズの最後の部分のムービー例では、「マトリックス © 」、QuickGrid
コンポーネント は次の HTML マークアップをレンダリングします (一部の要素と属性は表示を簡略化するために存在しません)。 明示的な Razor 式と補間された文字列によって、他のページへのリンクの href
値がどのように生成されたかを確認します。 データベース内の映画の識別子は、この例では 3
になっているため、id
は、3
、Edit
、および Details
ページのクエリ文字列で Delete
になります。 アプリを実行すると、別の値が表示される場合があります。
<table>
<thead>
<tr>
<th>Title</th>
<th>ReleaseDate</th>
<th>Genre</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>The Matrix</td>
<td>3/29/1999</td>
<td>Sci-fi (Cyberpunk)</td>
<td>5.00</td>
<td>
<a href="movies/edit?id=3">Edit</a> |
<a href="movies/details?id=3">Details</a> |
<a href="movies/delete?id=3">Delete</a>
</td>
</tr>
</tbody>
</table>
列名は Movie
モデルのプロパティから取得されるため、リリース日の単語間にスペースがありません。 単語間にスペースを含む値で Title に PropertyColumn<TGridItem,TProp> を追加します。
- <PropertyColumn Property="movie => movie.ReleaseDate" />
+ <PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
アプリを実行して、リリース日の 2 つの単語が列に表示されていることを確認します。
ブラウザーのウィンドウを閉じて、アプリを停止します。
ブラウザーのウィンドウを閉じ、VS Code でキーボードの Shift+F5 キーを押して、アプリを停止します。
ブラウザーのウィンドウを閉じ、コマンド シェルで Ctrl +押して、アプリを停止します。
Details
コンポーネント
Details
コンポーネント定義ファイル (Components/Pages/Movies/Details.razor
) を開きます。
ファイルの先頭にある @page
ディレクティブは、ページの相対 URL が /movies/details
であることを示します。 前と同様に、データベース コンテキストが挿入され、API (BlazorWebAppMovies.Models
と Microsoft.EntityFrameworkCore
) にアクセスするための名前空間が提供されます。 Details
コンポーネントでは、アプリの NavigationManager も挿入します。これは、コンポーネントのさまざまなナビゲーション関連操作に使用されます。
@page "/movies/details"
@using Microsoft.EntityFrameworkCore
@using BlazorWebAppMovies.Models
@inject IDbContextFactory<BlazorWebAppMovies.Data.BlazorWebAppMoviesContext> DbFactory
@inject NavigationManager NavigationManager
映画エンティティの詳細は、クエリ文字列内の識別子 (Id
) によって特定される映画が表示用に読み込まれている場合にのみ表示されます。 その映画が movie
に存在するかどうかは、@if
Razor ステートメントで確認されます。
@if (movie is null)
{
<p><em>Loading...</em></p>
}
映画が読み込まれると、説明リスト (MDN ドキュメント) として、次の 2 つのリンクと共に表示されます。
- 最初のリンクを使用すると、ユーザーはエンティティを編集することができます。
- 2 番目のリンクを使用すると、ユーザーは映画の
Index
ページに戻ることができます。
表示用の Razor マークアップを簡素化するために、次の例では CSS クラスは示されていません。
<dl>
<dt>Title</dt>
<dd>@movie.Title</dd>
<dt>ReleaseDate</dt>
<dd>@movie.ReleaseDate</dd>
<dt>Genre</dt>
<dd>@movie.Genre</dd>
<dt>Price</dt>
<dd>@movie.Price</dd>
</dl>
<div>
<a href="@($"/movies/edit?id={movie.Id}")">Edit</a> |
<a href="@($"/movies")">Back to List</a>
</div>
</div>
映画公開日の説明用語要素 (<dt>
) の内容にスペースを追加して、単語を区切ります。
- <dt class="col-sm-2">ReleaseDate</dt>
+ <dt class="col-sm-2">Release Date</dt>
コンポーネントの @code
ブロックの C# コードを調べます。
private Movie? movie;
[SupplyParameterFromQuery]
private int Id { get; set; }
protected override async Task OnInitializedAsync()
{
using var context = DbFactory.CreateDbContext();
movie = await context.Movie.FirstOrDefaultAsync(m => m.Id == Id);
if (movie is null)
{
NavigationManager.NavigateTo("notfound");
}
}
movie
変数は Movie
型のプライベート フィールドです。これは null 参照型 (?
) です。つまり、movie
が null
に設定される可能性があります。
Id
は、の存在によりコンポーネントのクエリ文字列から提供される[SupplyParameterFromQuery]
です。 識別子が見つからない場合、Id
の既定値は 0 (0
) になります。
OnInitializedAsync
は、これまで見てきた最初のコンポーネント ライフサイクル メソッドです。 このメソッドは、コンポーネントの読み込み時に実行されます。 データベースセット (FirstOrDefaultAsync) に対して DbSet<Movie>
が呼び出され、クエリ文字列で設定された Id
パラメーターと等しい Id
を持つ映画エンティティが取得されます。 movie
が null
の場合は、NavigationManager.NavigateTo を使用して notfound
エンドポイントに移動します。
アプリに実際の notfound
エンドポイント (Razor コンポーネント) はありません。 サーバー側レンダリング (SSR) を導入する場合、Blazor には 404 (Not Found) 状態コードを返すメカニズムがありません。 一時的な回避策として、存在しないエンドポイントに移動することで 404 が生成されます。 このスキャフォールディングされたコードは、エンティティが見つからない場合に適切な結果をさらに実装するためのものです。 たとえば、コンポーネントが、サポート チームに問い合わせを提出できるページにユーザーを誘導したり、挿入された NavigationManager および NavigationManager.NavigateTo コードを削除して、エンティティが見つからなかったというメッセージを表示する Razor マークアップおよびコードに置き換えたりすることができます。
Create
コンポーネント
Create
コンポーネント定義ファイル (Components/Pages/Movies/Create.razor
) を開きます。
このコンポーネントは、EditFormと呼ばれる組み込みコンポーネントを使用します。このコンポーネントはユーザー入力用のフォームをレンダリングし、検証機能を含んでいます。
次の例では、表示を簡素化するために CSS クラスが存在しません。
<EditForm method="post" Model="Movie" OnValidSubmit="AddMovie" FormName="create" Enhance>
<DataAnnotationsValidator />
<ValidationSummary role="alert" />
<div>
<label for="title">Title:</label>
<InputText id="title" @bind-Value="Movie.Title" />
<ValidationMessage For="() => Movie.Title" />
</div>
<div>
<label for="releasedate">ReleaseDate:</label>
<InputDate id="releasedate" @bind-Value="Movie.ReleaseDate" />
<ValidationMessage For="() => Movie.ReleaseDate" />
</div>
<div>
<label for="genre">Genre:</label>
<InputText id="genre" @bind-Value="Movie.Genre" />
<ValidationMessage For="() => Movie.Genre" />
</div>
<div>
<label for="price">Price:</label>
<InputNumber id="price" @bind-Value="Movie.Price" />
<ValidationMessage For="() => Movie.Price" />
</div>
<button type="submit">Create</button>
</EditForm>
ムービーのリリース日のラベル要素 (<label>
) の内容にスペースを追加して、単語を区切ります。
- <label for="releasedate" class="form-label">ReleaseDate:</label>
+ <label for="releasedate" class="form-label">Release Date:</label>
Model パラメーターには、モデル (この場合は Movie
) が割り当てられます。 OnValidSubmit は、フォームが送信されデータが有効な場合に呼び出すメソッド (AddMovie
) を指定します。 慣例により、ページに複数のフォームが存在する場合にフォームの競合を防ぐために、すべてのフォームに FormName を割り当てる必要があります。 Enhance フラグは、ページ全体の再読み込みを実行せずにフォームを送信するサーバー側レンダリング (SSR) のための Blazor 機能をアクティブにします。
検証用:
- DataAnnotationsValidator では、データ注釈検証のサポートが追加されます。これについては、このチュートリアル シリーズで後ほど説明します。
- ValidationSummary コンポーネントは、検証メッセージの一覧を表示します。
- ValidationMessage<TValue> コンポーネントでは、フォームのフィールドの検証メッセージを保持します。
Blazor には、EditForm や、InputText、InputDate<TValue>、InputNumber<TValue> などのさまざまな入力コンポーネントをはじめ、フォームの作成に役立つ複数のフォーム要素コンポーネントが含まれています。 各入力コンポーネントは、@bind-Value
Razor 構文を使用してモデル プロパティにバインドされます。ここで、Value
は各入力コンポーネントのプロパティです。
コンポーネントの @code
ブロックでは、Movie
を通じてフォームに関連付けられた [SupplyParameterFromForm]
コンポーネントパラメーターが C# コードに含まれています。
AddMovie
メソッド:
- フォームが送信されると呼び出されます。
- フォームの検証に合格した場合、フォームのモデル (
Movie
) にバインドされた映画データを追加します。 - SaveChangesAsync は、映画を保存するためにデータベース コンテキストで呼び出されます。
- NavigationManager は、ユーザーを映画の
Index
ページに戻すために使用されます。
@code {
[SupplyParameterFromForm]
private Movie Movie { get; set; } = new();
private async Task AddMovie()
{
using var context = DbFactory.CreateDbContext();
context.Movie.Add(Movie);
await context.SaveChangesAsync();
NavigationManager.NavigateTo("/movies");
}
}
警告
このチュートリアルでは、それはアプリの問題ではありませんが、フォーム データをエンティティ データ モデルにバインドすると、オーバーポスティング攻撃の影響を受ける可能性があります。 このトピックに関する追加情報は、この記事で後ほど取り上げます。
Delete
コンポーネント
Delete
コンポーネント定義ファイル (Components/Pages/Movies/Delete.razor
) を開きます。
映画公開日の説明用語要素 (<dt>
) の内容にスペースを追加して、単語を区切ります。
- <dt class="col-sm-2">ReleaseDate</dt>
+ <dt class="col-sm-2">Release Date</dt>
Razor の送信ボタンの EditForm マークアップを調べます (わかりやすくするために CSS クラスが削除されています)。
<button type="submit" disabled="@(movie is null)">Delete</button>
[Delete] ボタンは、明示的なdisabled
式 () を使用して、ムービーの存在 (null
ではない) に基づいてRazor@(...)
を設定します。
@code
ブロックの C# コードでは、DeleteMovie
メソッドはムービーを削除し、変更をデータベースに保存して、ユーザーをムービー Index
ページに移動します。 "ムービー" フィールド (movie!
) の感嘆符は、null 許容演算子 (C# 言語リファレンス) であり、movie
に対する null 許容警告を抑制します。
private async Task DeleteMovie()
{
using var context = DbFactory.CreateDbContext();
context.Movie.Remove(movie!);
await context.SaveChangesAsync();
NavigationManager.NavigateTo("/movies");
}
Edit
コンポーネント
Edit
コンポーネント定義ファイル (Components/Pages/Movies/Edit.razor
) を開きます。
ムービーのリリース日のラベル要素 (<label>
) の内容にスペースを追加して、単語を区切ります。
- <label for="releasedate" class="form-label">ReleaseDate:</label>
+ <label for="releasedate" class="form-label">Release Date:</label>
コンポーネントは、EditForm コンポーネントと同様の Create
を使用します。
ムービー エンティティの識別子 Id
は、フォームの隠しフィールドに格納されます。
<input type="hidden" name="Movie.Id" value="@Movie.Id" />
@code
ブロックの C# コードを調べます。
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");
}
private bool MovieExists(int id)
{
using var context = DbFactory.CreateDbContext();
return context.Movie.Any(e => e.Id == id);
}
ムービー エンティティの EntityState は Modified に設定されます。これは、エンティティがコンテキストによって追跡され、データベースに存在し、そのプロパティ値の一部またはすべてが変更されることを示します。
コンカレンシー例外があり、変更が保存された時点でムービー エンティティが存在しなくなった場合、コンポーネントは存在しないエンドポイント (notfound
) にリダイレクトされ、結果として 404 (Not Found) 状態コードが返されます。 このコードを変更して、ムービーがデータベースに存在しなくなったことをユーザーに通知したり、専用の Not Found コンポーネントを作成して、ユーザーをそのエンドポイントに移動したりできます。 ムービーが存在し、コンカレンシー例外がスローされた場合 (たとえば、別のユーザーがエンティティを既に変更している場合など)、throw
ステートメント (C# 言語リファレンス) を使用してコンポーネントによって例外が再スローされます。 EF Core アプリでの Blazor のコンカレンシーの処理に関する追加のガイダンスは、Blazor ドキュメントで提供されています。
警告
このチュートリアルでは、それはアプリの問題ではありませんが、フォーム データをエンティティ データ モデルにバインドすると、オーバーポスティング攻撃の影響を受ける可能性があります。 このトピックに関する追加情報は、次のセクションに表示されます。
過剰投稿攻撃を軽減する
Create
コンポーネントや Edit
コンポーネントのフォームのように静的にレンダリングされたサーバー側フォームは、一括割り当て攻撃とも呼ばれるオーバーポスティング攻撃に対して脆弱になる可能性があります。 オーバーポスト攻撃は、悪意のあるユーザーが、レンダリングされたフォームの一部ではなく、開発者がユーザーに修正を許可しないプロパティのデータを処理する HTML フォームの POST をサーバーに発行したときに発生します。 "オーバーポスティング" という用語は、文字どおり、悪意のあるユーザーがフォームで "過剰に" ポストすることを意味します。
このチュートリアルの Create
コンポーネントと Edit
コンポーネントの例では、Movie
モデルには作成操作と更新操作の制限付きプロパティが含まれていないため、オーバーポスティングは問題になりません。 ただし、今後作成したり修正したりする静的 SSR ベースの Blazor フォームで作業する場合は、オーバーポスティングに注意することが重要です。
オーバーポスティングを軽減するために、作成 (挿入) および更新操作でフォームとデータベースに対して個別のビュー モデルまたはデータ転送オブジェクト (DTO) を使用することをお勧めします。 フォームが送信されると、ビュー モデルまたは DTO のプロパティのみがコンポーネントと C# コードによって使用され、データベースが変更されます。 悪意のあるユーザーによって含まれる余分なデータは破棄されるため、悪意のあるユーザーによるオーバーポスティング攻撃を防ぎます。
完成したサンプルを使用したトラブルシューティング
チュートリアルの実行中にテキストから解決できない問題が発生した場合は、コードを、 Blazor サンプル リポジトリの完成したプロジェクトと比較します。
Blazor サンプル GitHub リポジトリ (dotnet/blazor-samples
)
最新バージョンのフォルダーを選択します。 このチュートリアルのプロジェクトのサンプル フォルダーには、BlazorWebAppMovies
という名前が付けられています。
その他のリソース
NavLink
コンポーネント- ASP.NET Core Blazor のレイアウト
- Razor ディレクティブ (Razor 構文記事) / Razor ディレクティブ (Blazor ドキュメント)
- ASP.NET Core Blazor 'QuickGrid' コンポーネント
- ASP.NET Core Blazor フォームの概要
- EF Core アプリでの Blazor の同時実行
- ASP.NET Core Blazor グローバリゼーションとローカライズ: コンマ番号区切り記号を受け入れる方法など、異なるカルチャや言語のユーザーにグローバル化およびローカライズされたコンテンツをレンダリングする方法について説明します。
次のステップ
ASP.NET Core