Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.
Эта статья является третьей частью Blazor учебника по приложению базы данных фильмов, которое учит вас основам создания ASP.NET Core Blazor Web App с функциями управления базой данных фильмов.
В этой части руководства рассматриваются Razor компоненты проекта, которые были сформированы в приложении. Внесены улучшения в отображение данных о фильме.
составные части компонента Razor.
Приложения Blazor основаны на компонентах Razor, которые часто называются просто компонентами. Компонентом называется любой элемент пользовательского интерфейса, например страница, диалоговое окно или форма ввода данных. Компоненты являются классами C# .NET, встроенными в сборки .NET.
Razor относится к тому, как компоненты обычно записываются в виде Razor страницы разметки (.razor расширения файла) для логики и композиции пользовательского интерфейса на стороне клиента. Синтаксис Razor сочетает разметку HTML с кодом C#, позволяя повысить производительность разработчиков.
Несмотря на то, что разработчики и онлайн-ресурсы используют термин "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 конструкцию в Razor компонентах, Razor директиву.
Razor Директива — это зарезервированное ключевое слово с @ префиксом, которое отображается в Razor разметке, изменяя способ компиляции или функциональность компонентов. Директива @pageRazor задает шаблон маршрута для компонента. Этот компонент достигается в браузере по относительному URL-адресу /welcome. По соглашению большинство директив компонента помещаются в начало файла определения компонента.
Компонент PageTitle является компонентом, встроенным в платформу, которая задает заголовок страницы.
"Welcome to Blazor!" — это первая отрисованная разметка текста компонента на содержимое элемента заголовка H1 (<h1>).
Затем приветственное сообщение отображается с помощью Razor синтаксиса путем добавления символа (@) в качестве префикса для переменной на C# (welcomeMessage).
Блок @code содержит код C# компонента.
welcomeMessage — это приватная строка, инициализированная некоторым значением.
В следующих разделах этой статьи:
- Описаны три компонента навигации и макета веб-страницы,
NavMenu, NavLink иMainLayoutкомпоненты. - Рассматриваются компоненты, созданные при создании шаблонов для операций CRUD с сущностями базы данных фильма.
NavMenu компонент для навигации
Компонент NavMenu (Components/Layout/NavMenu.razor) реализует навигацию на боковой панели с помощью NavLink компонентов, которые отображают ссылки навигации на другие Razor компоненты.
Компонент NavLink ведет себя как элемент <a>, за исключением того, что он переключает класс CSS active в зависимости от того, соответствует ли его href текущему URL-адресу. Класс active помогает пользователю понять, какая страница является активной страницей среди отображаемых ссылок навигации.
NavLinkMatch.All, присвоенный параметру Match, настраивает компонент так, чтобы он отображал активный класс CSS при совпадении с полным текущим URL-адресом.
Компонент NavLink встроен в платформу Blazor для любого Blazor приложения, в то время как NavMenu компонент является частью Blazor шаблонов проектов.
Components/Layout/NavMenu.razor:
<script type="module" src="@Assets["Components/Layout/NavMenu.razor.js"]"></script>
<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 id="nav-scrollable" class="nav-scrollable">
<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>
</nav>
</div>
<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="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>
</nav>
</div>
<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 компонент после внесения предыдущих изменений:
<script type="module" src="@Assets["Components/Layout/NavMenu.razor.js"]"></script>
<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 id="nav-scrollable" class="nav-scrollable">
<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>
<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>
<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="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>
Запустите приложение, чтобы увидеть обновленный бренд в верхней части боковой панели навигации и ссылку на страницу фильмов (фильмы):
Остановите приложение, закрыв окно браузера.
Остановите приложение, закрыв окно браузера и нажав клавиши SHIFT+F5 на клавиатуре в VS Code.
Остановите приложение, закрыв окно браузера и нажав клавиши CTRL+C в командной оболочке.
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>элемента включает:- Ссылка About , которая отправляет пользователя на целевую страницу документации по ASP.NET Core.
- Элемент
<article>с параметром Body (@Body) , где отрисовываются компоненты, использующие макет. - Пользовательский интерфейс ошибки (
<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.
@using Директивы, по-видимому, обращаются к следующему API:
- Microsoft.EntityFrameworkCore
- Microsoft.AspNetCore.Components.QuickGrid
BlazorWebAppMovies.ModelsBlazorWebAppMovies.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 свойств модели, поэтому дата выпуска не имеет пробела между словами. Добавьте в TitlePropertyColumn<TGridItem,TProp> значение, которое содержит пробел между словами:
- <PropertyColumn Property="movie => movie.ReleaseDate" />
+ <PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
Запустите приложение, чтобы увидеть, что столбец отображает два слова для даты выпуска.
Остановите приложение, закрыв окно браузера.
Остановите приложение, закрыв окно браузера и нажав клавиши SHIFT+F5 на клавиатуре в VS Code.
Остановите приложение, закрыв окно браузера и нажав клавиши CTRL+C в командной оболочке.
Компонент 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 проверяется с помощью инструкции @ifRazor :
@if (movie is null)
{
<p><em>Loading...</em></p>
}
При загрузке фильма он отображается в виде списка описаний (документация ПО MDN) вместе с двумя ссылками:
- Первая ссылка предоставляет пользователю возможность редактировать объект.
- Вторая ссылка позволяет пользователю вернуться на страницу фильмов
Index.
Классы CSS не отображаются в следующем примере, чтобы упростить разметку Razor для отображения:
<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>
Проверьте C# блока компонента @code :
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.NotFound();
}
}
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).
OnInitializedAsync является первым методом жизненного цикла компонентов, который мы видели. Этот метод выполняется при загрузке компонента.
FirstOrDefaultAsync вызывается в наборе базы данных (DbSet<Movie>) для получения сущности фильма с Id равным Id параметру, заданному строкой запроса. Если movie это nullтак, NavigationManager.NavigateTo используется для перехода к конечной точке notfound .
Если фильм не найден, вызов NavigationManager.NotFound приводит к отрисовке компонента NotFound, который создает страницу "Не найдено" в браузере с кодом состояния 404 (Не найдено).
В приложении нет фактической notfound конечной точки (Razor компонента). При внедрении серверной стороны рендеринга (SSR) в .NET 8 или .NET 9 приложение не имеет механизма возврата кода состояния 404 (Не найдено). 404 создается путем перехода к несуществующей конечной точке. Этот шаблонный код предназначен для дальнейшей реализации подходящего результата, если не найти сущность в .NET 8/9. Например, компонент может направить пользователя на страницу, где можно отправить запрос в службу поддержки, или удалить внедренный NavigationManager код и заменить его NavigationManager.NavigateTo разметкой и Razor кодом, отображающим сообщение пользователю, что сущность не найдена. В .NET 10 или более поздней версии Blazor есть встроенная функция Not Found. Дополнительные сведения см. в версии .NET 10 этого раздела.
Компонент 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 активирует Blazor функцию серверного рендеринга (SSR), которая отправляет форму без полной перезагрузки страницы.
Для проверки:
- DataAnnotationsValidator добавляет поддержку валидации аннотаций данных, которая будет рассмотрена далее в этом учебном руководстве.
- Компонент ValidationSummary отображает список сообщений проверки.
- Компоненты ValidationMessage<TValue> содержат сообщения проверки для полей формы.
Blazor включает несколько компонентов элемента формы для создания форм, включая и различные входные компоненты, такие как EditFormInputText, InputDate<TValue>и InputNumber<TValue>. Каждый входной компонент привязан к свойству модели с @bind-ValueRazor синтаксисом, где Value это свойство в каждом входном компоненте.
В блоке @code компонента код C# включает Movie параметр компонента, привязанный к форме с помощью атрибута[SupplyParameterFromForm].
Метод AddMovie:
- Вызывается при отправке формы.
- Добавляет данные фильма, привязанные к модели формы (
Movie), если проверка формы будет успешной. - Вызов SaveChangesAsync осуществляется в контексте базы данных для сохранения фильма.
-
NavigationManager используется для возврата пользователя на страницу фильмов
Index.
[SupplyParameterFromForm]
private Movie Movie { get; set; } = default!;
protected override void OnInitialized() => Movie ??= new();
private async Task AddMovie()
{
using var context = DbFactory.CreateDbContext();
context.Movie.Add(Movie);
await context.SaveChangesAsync();
NavigationManager.NavigateTo("/movies");
}
[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 HTML-атрибут на основе присутствия фильма (не 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");
}
Если фильм не найден, вызов NavigationManager.NotFound приводит к отрисовке компонента NotFound, который создает страницу "Не найдено" в браузере с кодом состояния 404 (Не найдено).
Компонент 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.NotFound();
}
else
{
throw;
}
}
NavigationManager.NavigateTo("/movies");
}
private bool MovieExists(int id)
{
using var context = DbFactory.CreateDbContext();
return context.Movie.Any(e => e.Id == id);
}
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 отслеживается контекстом, существует в базе данных и что некоторые или все его значения свойств изменяются.
Если фильм не найден, вызов NavigationManager.NotFound приводит к отрисовке компонента NotFound, который создает страницу "Не найдено" в браузере с кодом состояния 404 (Не найдено).
Если существует исключение параллелизма и сущность фильма больше не существует во время сохранения изменений, компонент перенаправляется на страницу "Не найдено", что приводит к возврату кода состояния 404 (не найдено). Если фильм есть и возникает исключение параллелизма, например, когда другой пользователь уже изменил сущность, исключение повторно выбрасывается компонентом с throw инструкцией (справочник по языку C#). Дополнительные рекомендации по обработке параллелизма с использованием EF Core в приложениях Blazor приведены в документации Blazor.
Если возникает ошибка параллелизма и сущность фильма больше не существует во время сохранения изменений, компонент перенаправляется на несуществующую конечную точку (notfound), что приводит к возврату статуса 404 (не найдено). Этот код можно изменить, чтобы уведомить пользователя о том, что фильм больше не существует в базе данных или создать выделенный компонент Not Found и перейти к этой конечной точке. Если фильм есть и возникает исключение параллелизма, например, когда другой пользователь уже изменил сущность, исключение повторно выбрасывается компонентом с throw инструкцией (справочник по языку C#). Дополнительные рекомендации по обработке параллелизма с использованием EF Core в приложениях Blazor приведены в документации Blazor.
Предупреждение
Несмотря на то, что приложение не является проблемой в этом руководстве, привязка данных формы к моделям данных сущностей может быть подвержена атакам перепоступления. Дополнительные сведения об этой теме отображаются в следующем разделе.
Смягчение атак чрезмерного размещения
Статически отрендеренные серверные формы, такие как в компонентах Create и Edit, могут быть уязвимы к атаке слишком массивного присвоения, также известной как атака массового присвоения. Атака сверхотправкой происходит, когда злоумышленник совершает отправку POST-запроса с HTML-формой на сервер, который обрабатывает данные для свойств, не входящих в отрисованную форму, и которые разработчик не хочет разрешать пользователям изменять. Термин "overposting" означает, что вредоносный пользователь чрезмерно использовал метод POST при работе с формой.
В примерах компонентов Create и Edit этого руководства модель Movie не включает ограниченные свойства для операций создания и обновления, поэтому избыточное добавление не является проблемой. Однако при работе со статическими формами на основе Blazor SSR важно помнить, что вы создаете и изменяете их в будущем.
Чтобы избежать избыточной передачи данных, рекомендуется использовать отдельную модель представления и объект передачи данных (DTO) для формы и базы данных в процессе операций создания (вставки) и обновления. При отправке формы для изменения базы данных используются только свойства модели представления или DTO компонента и кода C#. Все дополнительные данные, включенные злоумышленником, удаляются, поэтому злоумышленник не может проводить атаку с использованием избыточных данных.
Устранение неполадок с завершенным примером
Если при выполнении руководства возникла проблема, которую невозможно устранить только по тексту, сравните свой код с завершенным проектом в репозитории примеров Blazor.
Blazor
Выберите последнюю папку версии. Пример папки для проекта этого руководства называется BlazorWebAppMovies.
Дополнительные ресурсы
-
Компонент
NavLink - макеты ASP.NET Core Blazor
- Razor директивы (Razor статья синтаксиса) / Razor директивы (Blazor документация)
- компонент ASP.NET Core Blazor `QuickGrid` компонент
- Blazor Обзор основных форм ASP.NET
- Конкурентность с EF Core в приложениях Blazor
- ASP.NET Core глобализация и локализация: объясняется, как подавать глобализованное и локализованное содержимое пользователям в различных культурах и на разных языках, в том числе о том, как принимать разделители чисел в виде запятых.
Следующие шаги
ASP.NET Core