Программирование и разработка программного обеспечения

Как использовать unions и закрытые иерархии в ASP.NET Core

В .NET Blog объясняется, как использовать типы unions и закрытые иерархии с System.Text.Json через Minimal APIs, MVC, SignalR, Blazor и OpenAPI. Также определяются случаи неоднозначности и источники привязки, которые не поддерживают эти типы.

2026-09-10
5 мин. чтения
11 просмотров
فريق تحرير certi.news
Как использовать unions и закрытые иерархии в ASP.NET Core

ASP.NET Core в .NET 11 предоставляет практическую поддержку использования типов union и закрытых иерархий с System.Text.Json, позволяя представлять значение, которое может относиться к нескольким типам или к определённому семейству типов. Эта поддержка распространяется на Minimal APIs, контроллеры MVC, SignalR, Blazor и документацию OpenAPI, при этом существуют важные различия в способах чтения и записи, а также ограничения привязки параметров.

Когда подходит union?

Union подходит для контрактов, принимающих независимые альтернативы без необходимости в JSON-обёртке или поле discriminator. Например, значение может быть целым числом или строкой, либо конфигурация может принимать логическое значение или объект с более подробными параметрами. При сериализации активное состояние записывается непосредственно в естественном формате: специальная обёртка типа union и discriminator не добавляются.

Состояния, которые можно однозначно различить по структуре JSON, сериализуются автоматически. Однако если состояния имеют одинаковую структуру, например union, включающий объекты Cat и Dog, System.Text.Json не сможет определить тип только по JSON-токену. В этом случае можно предоставить пользовательский JsonTypeClassifier для выбора правильного состояния. То же относится к некоторым контрактам запросов, принимающим число или строку: специальные веб-настройки JSON позволяют читать числа из строк, из-за чего текст может интерпретироваться как любое из двух состояний.

Закрытые иерархии отличаются от union

Использование модификатора closed не означает, что полиморфная сериализация включится автоматически. При работе с конкретным производным типом, например PaymentAuthorized, System.Text.Json обрабатывает его как любой другой тип и создаёт JSON, содержащий paymentId и amount, без discriminator.

При использовании базового типа PaymentEvent в API необходимо указать производный тип, который должен быть создан. Это можно включить для базового типа с помощью JsonPolymorphic(InferClosedTypePolymorphism = true) или через JsonSerializerOptions.InferClosedTypePolymorphism. В этом случае System.Text.Json выводит производные типы в закрытой иерархии при сериализации и использует их имена в качестве идентификаторов, например $type со значением PaymentAuthorized.

Выбор между двумя моделями зависит от характера контракта: union представляет альтернативы, которые могут не принадлежать одному семейству, тогда как закрытые иерархии подходят для связанной группы событий или сущностей, которым нужен явный discriminator.

Поддержка на уровнях ASP.NET Core

В Minimal APIs union можно использовать в качестве параметра тела запроса или типа возвращаемого значения как в обычном конвейере выполнения, так и через Request Delegate Generator, при одинаковом поведении в обоих случаях. Union также может находиться внутри другой модели, быть элементом IAsyncEnumerable<T> или входить в контейнер, использующий AsParameters; настройки ConfigureHttpJsonOptions продолжают влиять на сериализацию.

Контроллеры MVC поддерживают unions в качестве параметров действий и возвращаемых типов, включая Task<TUnion> и ValueTask<TUnion>. Поскольку MVC использует настройки JsonSerializerDefaults.Web, проблема неоднозначности между числом и строкой во входных данных HTTP также применима к нему.

В SignalR поддержка работает через JsonHubProtocol для параметров, возвращаемых значений и элементов потоков. Union типа int или string не требует classifier при использовании этого протокола, поскольку JsonHubProtocol не считает строковый JSON-токен неоднозначным в данном случае. Однако unions, состояния которых используют один и тот же токен StartObject, например UnionPet(Cat, Dog), по-прежнему требуют classifier. SignalR не поддерживает эту возможность при использовании протоколов хабов MessagePack или Newtonsoft.Json.

Blazor и OpenAPI

Параметры компонентов Blazor внутри процесса передаются напрямую и поэтому не требуют сериализации. Однако взаимодействие с JavaScript, сохранённое состояние компонентов и параметры во время prerendering проходят через System.Text.Json и следуют тем же правилам для unions. Например, union можно передать в JavaScript как Boolean или объект параметров в зависимости от активного состояния, без добавления обёртки или discriminator.

В OpenAPI union представляется с помощью схемы anyOf, включающей по одному типу для каждого состояния. При самостоятельном появлении в качестве типов компоненты Cat и Dog повторно используют те же имена. Это отличается от полиморфных типов с discriminator, схемы которых публикуются под отдельными именами. ApiExplorer также может представить несколько типов ответа для одного и того же кода состояния и типа содержимого внутри anyOf.

Ограничения, которые необходимо учитывать

Поддержка unions зависит от System.Text.Json и поэтому не работает с источниками привязки, которые не проходят через анализ JSON. К ним относятся значения query string, значения маршрутов, заголовки и поля форм. Значения вроде ?id=42 недостаточно, чтобы определить, что именно имеется в виду: int, string или Guid. В Blazor это ограничение распространяется на [SupplyParameterFromQuery] и привязку из форм с использованием [SupplyParameterFromForm]. В источнике отмечается, что привязка union из этих источников всё ещё изучается, а отзывы по ней собираются.

Редакционный комментарий: Основная ценность здесь заключается не в добавлении нового формата JSON, а в унификации работы с многовариантными контрактами в широких частях экосистемы .NET 11. Однако успешность дизайна зависит от того, можно ли различить состояния, и от источника данных; поэтому следует заранее определить classifier или discriminator и не предполагать, что поддержка сериализации означает поддержку всех способов привязки параметров.

Источник новости
ف
Автор

فريق تحرير certi.news

В той же категории

Вам также может понравиться

Все новости