Start Debugging

MudBlazor 9.12: медленные ServerData и поиск в Autocomplete больше не перезаписывают более новые результаты

MudBlazor 9.12.0 исправляет давнюю гонку в MudAutocomplete, MudDataGrid и MudTable: побеждал результат поиска, завершившегося последним, а не запущенного последним. Разбираем, как работает исправление и что меняется в вашем коде ServerData и SearchFunc.

MudBlazor 9.12.0 вышел 29 октября 2026 года, и главная причина обновиться: состояние гонки, которое в том или ином виде было открыто со времён issue #3722. MudAutocomplete, MudDataGrid с ServerData и MudTable с ServerData сохраняли результат запроса, который завершился последним, а не того, который пользователь запустил последним.

Почему обычно побеждает широкий поиск

Введите “App” в автодополнение, подождите дольше задержки debounce, затем продолжите вводить “le Pie”. Теперь в работе два поиска. Короткий запрос находит больше строк, поэтому обычно он и медленнее. В 9.11 и ранее сначала отображаются результаты для “Apple Pie”, затем приходят результаты для “App” и заменяют их. Пользователь видит список, который не соответствует тексту в поле ввода.

То же происходило с фильтрами и пагинацией в таблице: достаточно быстро дважды изменить фильтр, и более старый, медленный вызов ServerData мог перезаписать более новую страницу.

Как 9.12 определяет устаревший результат

PR #13944 использует две разные стратегии. В MudAutocomplete каждому поиску присваивается номер версии, а поиск, замещённый более новым, завершается до того, как затронет элементы, счётчик возвращённых элементов или popover:

var searchVersion = ++_searchVersion;
// ... await SearchFunc(text, token) ...
if (searchVersion != _searchVersion)
{
    return;
}

Проверка выполняется дважды, второй раз после ReturnedItemsCountChanged, потому что ваш обработчик может уступить управление, и за это время успеет завершиться более новый поиск. Это важно, поскольку многие реализации SearchFunc полностью игнорируют CancellationToken, так что одной отменой это не исправить.

MudDataGrid использует токен, который и так передаёт в ServerData. Если более новая загрузка его отменила, старая загрузка возвращается, не присваивая данные и не сбрасывая Loading, даже если выбросила исключение:

catch (Exception) when (token.IsCancellationRequested)
{
    // A newer load cancelled this one, and it owns the data and the loading state.
    return;
}

Тип исключения намеренно игнорируется, потому что источники данных часто сообщают об отменённом запросе как о собственной ошибке (HttpRequestException, специфичный для провайдера DbException и так далее). PR #14053 применяет то же правило к MudTable, а последующие правки сохраняют индикатор загрузки при сбросе страницы и не дают позднему поиску заново открыть меню автодополнения, которое уже было закрыто, в котором сделан выбор или которое очищено.

Что меняется в вашем коде

В PR отмечены два изменения поведения:

Если вы оборачивали ReloadServerData() в try/catch, чтобы подавлять TaskCanceledException, этот код теперь мёртвый. Токен всё равно стоит передавать дальше: отменённый запрос, который действительно прекращается, обходится дешевле, чем тот, результат которого MudBlazor молча отбрасывает:

private async Task<GridData<Order>> LoadOrders(GridState<Order> state, CancellationToken ct)
{
    var page = await Http.GetFromJsonAsync<PagedResult<Order>>(
        $"api/orders?page={state.Page}&size={state.PageSize}", ct);
    return new GridData<Order> { Items = page!.Items, TotalItems = page.Total };
}

Пути VirtualizeServerData это исправление не затрагивает, так как у каждого виртуализированного запроса уже есть собственный токен.

Другие замечания по обновлению до 9.12.0

Столбцы-перечисления в MudDataGrid теперь предлагают фильтр “is any of”, у которого Value является коллекцией, поэтому обработчику ServerData, который выбирает логику по операторам, нужен новый case или Column.FilterOperators, чтобы скрыть этот оператор. Чтение MudStep.Title, MudDatePicker.MinDate и ещё нескольких свойств через ссылку на компонент теперь вызывает диагностику анализатора MUD0012; используйте вместо этого GetState(x => x.Title). Полный список, включая null-точки на линейных графиках и MudText.Truncate, приведён в примечаниях к релизу.

dotnet add package MudBlazor --version 9.12.0

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Назад