Поделиться через


компонент ASP.NET Core BlazorQuickGrid

Примечание.

Это не последняя версия этой статьи. В текущем выпуске смотрите версию .NET 9 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске смотрите версию этой статьи для .NET 9.

Компонент QuickGrid — это компонент Razor для быстрого и эффективного отображения данных в табличной форме. QuickGrid предоставляет простой и удобный компонент сетки данных для распространенных сценариев отрисовки сетки и служит эталонной архитектурой и базовым показателем производительности для создания компонентов сетки данных. QuickGrid высоко оптимизирован и использует передовые методы для достижения оптимальной производительности работы с графикой.

Пакет

Добавьте ссылку на пакет Microsoft.AspNetCore.Components.QuickGrid.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Пример приложения

Сведения о различных демонстрациях QuickGrid см. в разделе QuickGrid для Blazor примера приложения. Демонстрационный сайт размещен на страницах GitHub. Сайт загружается быстро благодаря статической предварительной подготовке с помощью поддерживаемого сообществом проекта GitHub.

QuickGrid реализация

Чтобы реализовать QuickGrid компонент, выполните следующие действия.

  • Укажите теги для компонента QuickGrid в разметке Razor (<QuickGrid>...</QuickGrid>).
  • Назовите запрашиваемый источник данных для сетки. Используйте любой из следующих источников данных:
    • Items: значение IQueryable<TGridItem>NULL, где TGridItem тип данных, представленных каждой строкой в сетке.
    • ItemsProvider: обратный вызов, предоставляющий данные для таблицы.
  • Class: необязательное имя класса CSS. Если указано, имя класса включается в атрибут class отображаемой таблицы.
  • Theme: имя темы (значение по умолчанию: default). Это влияет на то, какие правила стилизации соответствуют таблице.
  • Virtualize: если true, сетка рендерится с помощью виртуализации. Обычно это используется в сочетании с прокруткой и приводит к тому, что сетка извлекает и отрисовывает только данные вокруг текущего окна просмотра прокрутки. Это может значительно повысить производительность при прокрутке больших наборов данных. Если используется Virtualize, необходимо указать значение ItemSize и убедиться, что каждая строка отображается с постоянной высотой. Как правило, предпочтительнее не использовать Virtualize, если количество данных невелико или используется разбиение на страницы.
  • ItemSize: применимо только при использовании Virtualize. ItemSize определяет ожидаемую высоту в пикселях для каждой строки, позволяя механизму виртуализации получить правильное количество элементов для сопоставления размера дисплея и обеспечить точную прокрутку.
  • ItemKey: опционально определяет значение для @key на каждой отрисованной строке. Как правило, это используется для указания уникального идентификатора, например значения первичного ключа для каждого элемента данных. Это позволяет сетке сохранять связь между элементами строк и элементами данных на основе их уникальных идентификаторов, даже если TGridItem экземпляры заменяются новыми копиями (например, после нового запроса к базовому хранилищу данных). Если этот параметр не задан, @key является экземпляром TGridItem.
  • OverscanCount: определяет количество дополнительных элементов для отрисовки до и после видимого региона, чтобы уменьшить частоту отрисовки во время прокрутки. Хотя более высокие значения могут улучшить плавность прокрутки благодаря отрисовке большего количества элементов вне экрана, они также могут привести к увеличению времени начальной загрузки. Рекомендуется найти баланс на основе размера набора данных и требований к пользовательскому интерфейсу. Значение по умолчанию равно 3. Доступно только при использовании Virtualize.
  • Pagination: при необходимости связывает этот TGridItem экземпляр с PaginationState моделью, что приводит к отрисовке только текущей страницы данных. Обычно это используется в сочетании с компонентом Paginator или другой логикой пользовательского интерфейса, которая отображает и обновляет предоставленный PaginationState экземпляр.
  • В дочернем содержимом QuickGrid (RenderFragment) укажите PropertyColumn<TGridItem,TProp>столбцы, ячейки которых отображают TGridItem значения:
    • Property: определяет значение, отображаемое в ячейках этого столбца.
    • Format: необязательно указывает строку формата для значения. Использование Format требует, чтобы тип TProp реализовал IFormattable.
    • Sortable: указывает, должны ли данные сортироваться по этому столбцу. Значение по умолчанию может отличаться в зависимости от типа столбца. Например, TemplateColumn<TGridItem> сортируется, если указан любой параметр SortBy.
    • InitialSortDirection: указывает направление сортировки, если IsDefaultSortColumn есть true.
    • IsDefaultSortColumn: указывает, следует ли отсортировать этот столбец по умолчанию.
    • PlaceholderTemplate: Если указано, виртуальные сетки используют этот шаблон для визуализации ячеек, данные которых не были загружены.
    • HeaderTemplate: необязательный шаблон для ячейки заголовка этого столбца. Если не указано, шаблон заголовка по умолчанию включает в себя Title, а также все применимые индикаторы сортировки и кнопки параметров.
    • Title: Заголовок столбца. Заголовок отображается автоматически, если HeaderTemplate не используется.
  • Укажите теги для компонента QuickGrid в разметке Razor (<QuickGrid>...</QuickGrid>).
  • Назовите запрашиваемый источник данных для сетки. Используйте любой из следующих источников данных:
    • Items: обнуляемый IQueryable<TGridItem>, где TGridItem является типом данных, представленных каждой строкой в таблице.
    • ItemsProvider: обратный вызов, предоставляющий данные для сетки.
  • Class: необязательное имя класса CSS. Если указано, имя класса включается в атрибут class сформированной таблицы.
  • Theme: имя темы (значение по умолчанию: default). Это влияет на то, какие правила стилизации соответствуют таблице.
  • Virtualize: если выставлено значение true, сетка отрисовывается с использованием виртуализации. Обычно это используется в сочетании с прокруткой и приводит к тому, что сетка извлекает и отрисовывает только данные вокруг текущего окна просмотра прокрутки. Это может значительно повысить производительность при прокрутке больших наборов данных. Если используется Virtualize, необходимо указать значение ItemSize и убедиться, что каждая строка отображается с постоянной высотой. Как правило, предпочтительнее не использовать Virtualize, если объем данных невелик или используется постраничная навигация.
  • ItemSize: применимо только при использовании Virtualize. ItemSize определяет ожидаемую высоту в пикселях для каждой строки, позволяя механизму виртуализации получить правильное количество элементов для сопоставления размера дисплея и обеспечить точную прокрутку.
  • ItemKey: Опционально определяет значение для @key на каждой отрисованной строке. Как правило, это используется для указания уникального идентификатора, например значения первичного ключа для каждого элемента данных. Это позволяет сетке сохранять связь между элементами строк и элементами данных на основе их уникальных идентификаторов, даже если TGridItem экземпляры заменяются новыми копиями (например, после нового запроса к базовому хранилищу данных). Если этот параметр не задан, @key является экземпляром TGridItem.
  • Pagination: при необходимости связывает этот TGridItem экземпляр с PaginationState моделью, что обеспечивает выборку и отрисовку только текущей страницы данных. Обычно это используется в сочетании с компонентом Paginator или другой логикой пользовательского интерфейса, которая отображает и обновляет предоставленный PaginationState экземпляр.
  • В дочернем содержимом QuickGrid (RenderFragment) укажите PropertyColumn<TGridItem,TProp>столбцы, ячейки которых отображают TGridItem значения:
    • Property: определяет значение, отображаемое в ячейках этого столбца.
    • Format: необязательно указывает строку формата для значения. Использование Format требует, чтобы тип TProp реализовывал IFormattable.
    • Sortable: указывает, должны ли данные сортироваться по этому столбцу. Значение по умолчанию может отличаться в зависимости от типа столбца. Например, TemplateColumn<TGridItem> сортируется, если указан какой-либо параметр SortBy.
    • InitialSortDirection: указывает направление сортировки, если IsDefaultSortColumn есть true.
    • IsDefaultSortColumn: указывает, следует ли отсортировать этот столбец по умолчанию.
    • PlaceholderTemplate: Если указано, виртуализированные сетки используют этот шаблон для визуализации ячеек, данные которых не были загружены.
    • HeaderTemplate: необязательный шаблон для ячейки заголовка этого столбца. Если он не указан, шаблон заголовка по умолчанию включает в себя все применимые индикаторы сортировки и кнопки параметров, а также Title.
    • Title: текст заголовка для столбца. Заголовок отображается автоматически, если HeaderTemplate не используется.

Например, добавьте следующий компонент для отрисовки сетки.

Для Blazor Web Apps QuickGrid компонент должен использовать интерактивный режим отрисовки, чтобы включить интерактивные функции, такие как разбиение по страницам и сортировка.

PromotionGrid.razor:

@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid

<PageTitle>Promotion Grid</PageTitle>

<h1>Promotion Grid Example</h1>

<QuickGrid Items="people">
    <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
    <PropertyColumn Property="@(p => p.Name)" Sortable="true" />
    <PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>

@code {
    private record Person(int PersonId, string Name, DateOnly PromotionDate);

    private IQueryable<Person> people = new[]
    {
        new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
        new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
        new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
        new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
        new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
        new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
    }.AsQueryable();
}
@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid

<PageTitle>Promotion Grid</PageTitle>

<h1>Promotion Grid Example</h1>

<QuickGrid Items="people">
    <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
    <PropertyColumn Property="@(p => p.Name)" Sortable="true" />
    <PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>

@code {
    private record Person(int PersonId, string Name, DateOnly PromotionDate);

    private IQueryable<Person> people = new[]
    {
        new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
        new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
        new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
        new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
        new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
        new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
    }.AsQueryable();
}

Откройте доступ к компоненту в браузере по относительному пути /promotion-grid.

В настоящий момент нет планов расширения QuickGrid с добавлением функций, которые обычно предлагают полнофункциональные коммерческие сетки, например, иерархические строки, перетаскивание столбцов для их переупорядочивания или выбор диапазона, как в Excel. Если вам нужны расширенные функции, которые вы не хотите разрабатывать самостоятельно, продолжайте использовать сторонние сетки.

Сортировка по столбцу

Компонент QuickGrid может сортировать элементы по столбцам. Для Blazor Web Appсортировки требуется, чтобы компонент принял интерактивный режим отрисовки.

Добавьте Sortable="true" (Sortable) в PropertyColumn<TGridItem,TProp> тег:

<PropertyColumn Property="..." Sortable="true" />

В приложении отсортируйте столбец QuickGrid, выбрав заголовок этого столбца.

Элементы страницы с компонентом Paginator

Компонент QuickGrid может работать с данными постранично из источника данных. Для Blazor Web Appразбиения на страницы требуется, чтобы компонент принял интерактивный режим отрисовки.

Добавьте экземпляр PaginationState в блок @code компонента. Установите ItemsPerPage на количество элементов для отображения на странице. В следующем примере экземпляр называется pagination, а для каждой страницы задано десять элементов:

PaginationState pagination = new PaginationState { ItemsPerPage = 10 };

Задайте свойству Pagination компонента QuickGrid значение pagination:

<QuickGrid Items="..." Pagination="pagination">

Чтобы предоставить пользовательский интерфейс для разбиения на страницы, добавьте Paginator компонент выше или ниже QuickGrid компонента. Задайте Paginator.State на pagination:

<Paginator State="pagination" />

В приложении, используйте визуализированный компонент Paginator, чтобы пролистывать элементы.

QuickGrid отображает дополнительные пустые строки для заполнения конечной страницы данных при использовании с компонентом Paginator. В .NET 9 или более поздней версии пустые ячейки данных (<td></td>) добавляются в пустые строки. Пустые строки предназначены для облегчения отрисовки QuickGrid с одинаковой высотой строк и единым стилем на всех страницах.

Примените стили строк

Примените стили к строкам с помощью изоляции CSS, которая может включать в себя стилизацию пустых строк для компонентов QuickGrid, которые отображают данные с помощью компонента Paginator.

Обертите компонент QuickGrid в оберточный блочный элемент, например <div>.

+ <div>
    <QuickGrid ...>
        ...
    </QuickGrid>
+ </div>

Примените стиль строки с ::deepпсевдоэлементом. В следующем примере высота строки имеет значение 2em, в том числе для пустых строк данных.

{COMPONENT}.razor.css:

::deep tr {
    height: 2em;
}

Кроме того, используйте следующий подход к стилю CSS:

  • Отображение ячеек строк, заполненных данными.
  • Не отображайте пустые ячейки строк, чтобы избежать отрисовки их границ в соответствии со стилями Bootstrap.

{COMPONENT}.razor.css:

::deep tr:has(> td:not(:empty)) > td {
    display: table-cell;
}

::deep td:empty {
    display: none;
}

Дополнительные сведения об использовании ::deepпсевдоэлементов с изоляцией CSS см. в разделе изоляции CSS в ASP.NET Core Blazor.

Настраиваемые атрибуты и стили

QuickGrid также поддерживает передачу пользовательских атрибутов и классов стилей (Class) в отрисованный элемент таблицы:

<QuickGrid Items="..." custom-attribute="value" Class="custom-class">

Стилизация строки таблицы в зависимости от элемента строки

Примените класс стиля к строке таблицы на основе элемента строки, используя параметр RowClass.

В следующем примере :

  • Элемент строки представляется записью Person. Запись Person содержит свойство FirstName.
  • Метод GetRowCssClass применяет стили классов highlight-row к любой строке, где имя пользователя — "Julie".
<QuickGrid ... RowClass="GetRowCssClass">
    ...
</QuickGrid>

@code {
    private record Person(int PersonId, string FirstName, string LastName);

    private string GetRowCssClass(Person person) =>
        person.FirstName == "Julie" ? "highlight-row" : null;
}

Источник данных Entity Framework Core (EF Core)

Используйте шаблон фабрики для определения контекста EF Core базы данных, который предоставляет данные компоненту QuickGrid. Дополнительные сведения о том, почему шаблон фабрики рекомендуется, см. в разделе ASP.NET Core Blazor с Entity Framework Core (EF Core).

Фабрика контекста базы данных (IDbContextFactory<TContext>) внедряется в компонент с помощью директивы @inject. Подход фабрики требует освобождения контекста базы данных, поэтому компонент реализует интерфейс IAsyncDisposable с помощью директивы @implements. Поставщик элементов для компонента QuickGrid — это DbSet<T>, полученный из созданного контекста базы данных (CreateDbContext) фабрики контекста базы данных, внедренной в систему.

QuickGrid распознает экземпляры, предоставляемые EF IQueryable, и умеет асинхронно разрешать запросы, чтобы повысить эффективность.

Добавьте ссылку на Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter пакет NuGet.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Вызовите AddQuickGridEntityFrameworkAdapter в коллекции служб в файле Program, чтобы зарегистрировать реализацию IAsyncQueryExecutor с поддержкой EF.

builder.Services.AddQuickGridEntityFrameworkAdapter();

В следующем примере используется ExampleTableDbSet<TEntity> (таблица) из контекста AppDbContext базы данных (context) в качестве источника данных для QuickGrid компонента:

@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<AppDbContext> DbFactory

...

<QuickGrid ... Items="context.ExampleTable" ...>
    ...
</QuickGrid>

@code {
    private AppDbContext context = default!;

    protected override void OnInitialized()
    {
        context = DbFactory.CreateDbContext();
    }

    public async ValueTask DisposeAsync() => await context.DisposeAsync();
}

В блоке кода (@code) предыдущего примера:

  • Поле context содержит контекст базы данных, типизированный как .AppDbContext
  • Метод OnInitialized жизненного цикла назначает новому контексту базы данных (CreateDbContext) поле context из внедренной фабрики (DbFactory).
  • Асинхронный DisposeAsync метод удаляет контекст базы данных при удалении компонента.

Вы также можете использовать любой оператор LINQ, поддерживаемый EF, чтобы отфильтровать данные перед передачей их в параметр Items.

В следующем примере фильмы фильтруется по названию фильма, введенном в поле поиска. Контекст базы данных — BlazorWebAppMoviesContextи модель Movie. Свойство фильма Title используется для операции фильтра.

@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<BlazorWebAppMoviesContext> DbFactory

...

<p>
    <input type="search" @bind="titleFilter" @bind:event="oninput" />
</p>

<QuickGrid ... Items="FilteredMovies" ...>
    ...
</QuickGrid>

@code {
    private string titleFilter = string.Empty;
    private BlazorWebAppMoviesContext context = default!;

    protected override void OnInitialized()
    {
        context = DbFactory.CreateDbContext();
    }

    private IQueryable<Movie> FilteredMovies => 
        context.Movie.Where(m => m.Title!.Contains(titleFilter));

    public async ValueTask DisposeAsync() => await context.DisposeAsync();
}

Пример работы см. в следующих ресурсах:

Поддержка отображаемого имени

Название столбца можно назначить с помощью ColumnBase<TGridItem>.Title тега PropertyColumn<TGridItem,TProp>. В следующем примере с фильмом столбец назван "Release Date" для данных о дате выхода фильма.

<PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />

Тем не менее, управление именами столбцов через свойства привязанной модели обычно является более подходящим для поддержания приложения. Модель может контролировать отображаемое имя свойства с помощью атрибута[Display]. В следующем примере для свойства ReleaseDate модели указывается отображаемое имя даты выпуска фильма "Release Date".

[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }

Чтобы компонент QuickGrid мог использовать свойство DisplayAttribute.Name, создайте подкласс PropertyColumn<TGridItem,TProp>, либо в компоненте, либо в отдельном классе. Вызовите метод GetName, чтобы вернуть локализованное значение DisplayAttribute.Name, если нелокализованный DisplayName ([DisplayName] атрибут) не имеет значения:

public class DisplayNameColumn<TGridItem, TProp> : PropertyColumn<TGridItem, TProp>
{
    protected override void OnParametersSet()
    {
        if (Title is null && Property.Body is MemberExpression memberExpression)
        {
            var memberInfo = memberExpression.Member;
            Title = 
                memberInfo.GetCustomAttribute<DisplayNameAttribute>().DisplayName ??
                memberInfo.GetCustomAttribute<DisplayAttribute>().GetName() ??
                memberInfo.Name;
        }

        base.OnParametersSet();
    }
}

Используйте подкласс в компоненте QuickGrid . В следующем примере используется предыдущий DisplayNameColumn . Имя "Release Date" предоставляется атрибутом [Display] в модели, поэтому нет необходимости указывать Title.

<DisplayNameColumn Property="movie => movie.ReleaseDate" />

Атрибут также поддерживается:

[DisplayName("Release Date")]
public DateTime ReleaseDate { get; set; }

[Display] Однако этот атрибут рекомендуется, так как он предоставляет дополнительные свойства. Например, [Display] атрибут предлагает возможность назначать тип ресурса для локализации.

Удаленные данные

В Blazor WebAssembly приложениях получение данных из веб-API на основе JSON на сервере является общим требованием. Чтобы получить только данные, необходимые для текущей страницы или представления данных, и применить правила сортировки или фильтрации на сервере, используйте ItemsProvider этот параметр.

ItemsProvider также можно использовать в серверном Blazor приложении, если приложению требуется запрашивать внешнюю конечную точку или в других случаях, когда требования не охватываются IQueryable.

Укажите обратный вызов, соответствующий типу GridItemsProvider<TGridItem> делегата, где TGridItem тип данных, отображаемых в сетке. Обратный вызов присваивается параметру типа GridItemsProviderRequest<TGridItem>, который задает начальный индекс, максимальное число строк и порядок сортировки возвращаемых данных. Помимо возврата соответствующих элементов, общее количество элементов (totalItemCount) также требуется для правильного функционирования постраничного доступа и виртуализации.

Следующий пример получает данные из общедоступной базы данных OpenFDA по контролю за продуктами.

Объект GridItemsProvider<TGridItem> преобразует GridItemsProviderRequest<TGridItem> в запрос к базе данных OpenFDA. Параметры запроса превратятся в определенный формат URL-адреса, поддерживаемый внешним API JSON. Выполнять сортировку и фильтрацию можно только через сортировку и фильтрацию, поддерживаемую внешним API. Конечная точка OpenFDA не поддерживает сортировку, поэтому ни один из столбцов не помечен как сортируемый. Однако она поддерживает пропуск записей (skip параметр) и ограничение возврата записей (limit параметр), таким образом, компонент может включать виртуализацию и быстро прокручивать десятки тысяч записей.

FoodRecalls.razor:

@page "/food-recalls"
@inject HttpClient Http
@inject NavigationManager Navigation

<PageTitle>Food Recalls</PageTitle>

<h1>OpenFDA Food Recalls</h1>

<div class="grid" tabindex="-1">
    <QuickGrid ItemsProvider="@foodRecallProvider" Virtualize="true">
        <PropertyColumn Title="ID" Property="@(c => c.Event_Id)" />
        <PropertyColumn Property="@(c => c.State)" />
        <PropertyColumn Property="@(c => c.City)" />
        <PropertyColumn Title="Company" Property="@(c => c.Recalling_Firm)" />
        <PropertyColumn Property="@(c => c.Status)" />
    </QuickGrid>
</div>

<p>Total: <strong>@numResults results found</strong></p>

@code {
    private GridItemsProvider<FoodRecall>? foodRecallProvider;
    private int numResults;

    protected override async Task OnInitializedAsync()
    {
        foodRecallProvider = async req =>
        {
            var url = Navigation.GetUriWithQueryParameters(
                "https://api.fda.gov/food/enforcement.json", 
                new Dictionary<string, object?>
            {
                { "skip", req.StartIndex },
                { "limit", req.Count },
            });

            var response = await Http.GetFromJsonAsync<FoodRecallQueryResult>(
                url, req.CancellationToken);

            return GridItemsProviderResult.From(
                items: response!.Results,
                totalItemCount: response!.Meta.Results.Total);
        };

        numResults = (await Http.GetFromJsonAsync<FoodRecallQueryResult>(
            "https://api.fda.gov/food/enforcement.json"))!.Meta.Results.Total;
    }
}

Дополнительные сведения о вызове веб-API см. в статье Вызов веб-API из приложения ASP.NET CoreBlazor.

QuickGrid шаблон

Инструмент QuickGrid создаёт каркасы для компонентов Razor с помощью QuickGrid для отображения данных из базы данных.

Шаблон создает базовые страницы создания, чтения, обновления и удаления (CRUD) на основе модели данных Entity Framework Core. Вы можете сформировать отдельные страницы или все страницы CRUD. Вы выбираете класс модели и DbContext, при необходимости создаете новый DbContext.

Компоненты шаблонов Razor добавляются в проект в созданной папке с именем класса модели. Созданный Index компонент использует компонент QuickGrid для отображения данных. Настройте созданные компоненты по мере необходимости и включите интерактивность, чтобы воспользоваться интерактивными функциями, например, постраничной навигацией, сортировкой и фильтрацией.

Компоненты, созданные генератором кода, требуют отрисовки на стороне сервера (SSR), поэтому они не поддерживаются при работе в среде WebAssembly.

Щелкните правой кнопкой мыши папку Components/Pages и выберите "Добавить>новый шаблонный элемент".

При открытии диалогового окна "Добавление нового шаблона", откройте вкладку Установленные Общие Компоненты и выберите Компоненты с использованием Entity Framework (CRUD). Нажмите кнопку Добавить.

CRUD — это акроним для создания, чтения, обновления и удаления. Шаблон создает, редактирует, удаляет, детали и компоненты индекса для приложения.

Заполните диалоговое окно "Добавление Razor компонентов" с помощью Entity Framework (CRUD):

  • Раскрывающийся список шаблонов включает другие шаблоны для создания, редактирования, удаления, отображения сведений и компонентов списка. Этот раскрывающийся список пригодится, когда нужно только создать конкретный тип компонента, сформированного по классу модели. Оставьте раскрывающийся список Template в значении CRUD, чтобы создать полный набор компонентов.
  • В раскрывающемся списке класса модели выберите класс модели. Папка создается для созданных компонентов из имени модели (если класс модели называется Movie, папка автоматически называется MoviePages).
  • Длякласса DbContext выполните один из следующих подходов:
    • Выберите существующий класс DbContext, о котором вы знаете, что он имеет регистрацию поставщика фабрики (AddDbContextFactory).
    • Выберите кнопку + (знак плюс) и используйте модальное диалоговое окно Добавление контекста данных, чтобы указать новое имя класса DbContext, который нужно зарегистрировать через поставщика фабрики, вместо того чтобы использовать тип контекста напрямую в качестве регистрации службы.
  • После закрытия диалогового окна модели раскрывающийся список поставщика базы данных по умолчанию используется для SQL Server. Вы можете выбрать подходящий поставщик для используемой базы данных. К ним относятся SQL Server, SQLite, PostgreSQL и Azure Cosmos DB.
  • Выберите Добавить.

Пример использования каркаса QuickGrid см. в статье Создание приложения для базы данных фильмов (обзор).

Несколько одновременных запросов EF Core активируют System.InvalidOperationException

Несколько одновременных запросов EF Core могут вызвать следующие System.InvalidOperationException:

System.InvalidOperationException: вторая операция была запущена в этом экземпляре контекста до завершения предыдущей операции. Обычно это вызвано тем, что разные потоки одновременно используют один и тот же экземпляр DbContext. Дополнительные сведения о том, как избежать проблем с многопоточностью в DbContext, см. в https://go.microsoft.com/fwlink/?linkid=2097913.

Этот сценарий планируется улучшить в предстоящем выпуске ASP.NET Core. Для получения дополнительной информации см. [Blazor] Улучшение взаимодействия с QuickGrid и EF Core (dotnet/aspnetcore #58716).

В то же время вы можете устранить проблему, используя ItemsProvider с маркером отмены. Маркер отмены предотвращает одновременные запросы, отменяя предыдущий запрос при выдаче нового запроса.

Рассмотрим следующий пример, который основан на компоненте базы данных фильмов Index и который предназначен для приложения по созданию базы данных фильмов Blazor (Обзор) руководства. Более простую версию, встроенную в приложение, можно увидеть в примере приложения статьи. Компонент Index, встроенный в приложение, заменяется следующим компонентом.

Components/Pages/MoviePages/Index.razor:

@page "/movies"
@rendermode InteractiveServer
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.QuickGrid
@using BlazorWebAppMovies.Models
@using BlazorWebAppMovies.Data
@inject IDbContextFactory<BlazorWebAppMovies.Data.BlazorWebAppMoviesContext> DbFactory

<PageTitle>Index</PageTitle>

<h1>Index</h1>

<div>
    <input type="search" @bind="titleFilter" @bind:event="oninput" />
</div>

<p>
    <a href="movies/create">Create New</a>
</p>

<div>
    <QuickGrid Class="table" TGridItem="Movie" ItemsProvider="GetMovies"
            ItemKey="(x => x.Id)" Pagination="pagination">
        <PropertyColumn Property="movie => movie.Title" Sortable="true" />
        <PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
        <PropertyColumn Property="movie => movie.Genre" />
        <PropertyColumn Property="movie => movie.Price" />
        <PropertyColumn Property="movie => movie.Rating" />

        <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>
</div>

<Paginator State="pagination" />

@code {
    private BlazorWebAppMoviesContext context = default!;
    private PaginationState pagination = new PaginationState { ItemsPerPage = 5 };
    private string titleFilter = string.Empty;

    public async ValueTask<GridItemsProviderResult<Movie>> GetMovies(GridItemsProviderRequest<Movie> request)
    {
        using var context = DbFactory.CreateDbContext();
        var totalCount = await context.Movie.CountAsync(request.CancellationToken);
        IQueryable<Movie> query = context.Movie.OrderBy(x => x.Id);
        query = request.ApplySorting(query).Skip(request.StartIndex);

        if (request.Count.HasValue)
        {
            query = query.Take(request.Count.Value);
        }

        var items = await query.ToArrayAsync(request.CancellationToken);

        var result = new GridItemsProviderResult<Movie>
        {
            Items = items,
            TotalItemCount = totalCount
        };

        return result;
    }
}