Найти в Дзене
Архитектура на .NET

Использование record в качестве Command/Query моделей ASP.NET контроллера

Оглавление

В C# 9 появилось новое ключевое слово record для обозначения неизменяемых (immutable) типов данных. Это значит, что вы не можете поменять свойство такого объекта без создания нового экземпляра. Классический пример подобного поведение — хорошо известный тип DateTime.

Вместе с ключевым словом добавился и упрощённый синтаксис по объявлению таких типов данных:

Объявленные таким сокращенным образом типы данных очень удобно использовать в качестве моделей для запросов и ответов в API. Например:

-2

Здесь UserQuery представляет собой модель данных запроса, и в то же время при использовании паттерна CQRS эту же самую модель данных можно передать в обработчик (пример с использованием библиотеки MediatR будет ниже).

Модификация "немодифицируемого" типа record

Выше мы рассмотрели пример запроса (query), а теперь рассмотрим пример команды (command). Допустим, нам нужно сделать API для изменения имени пользователя по его id.

Команда на обновление пользователя будет выглядеть так:

-3

А API, по которому мы хотим обновлять пользователя, будет выглядеть примерно так:

-4

Т.е. получается, что мы должны собрать нашу команду из двух источников: id взять из пути запроса, а name из его тела. ASP.NET не поддерживает автоматический биндинг модели из разных источников, так что нам придется собирать её самостоятельно. Но как, если наша модель немодифицируемая?

С помощью ключевого слова with:

-5

Пример реализации API для обновления имени пользователя с использованием MediatR и record в качестве команды

-6

И ещё кое-что важное, если вы используете Swagger

Если вы используете Swagger, то в генерируемом им описании вашего API при таком подходе тело PUT запроса на обновление пользователя будет содержать поле id, дублирующее id из URL. Чтобы этого избежать, достаточно в вашей модели пометить поле id атрибутом JsonIgnore.

Но есть нюанс. Напрямую атрибут JsonIgnore нельзя применить к "параметру конструктора" при использовании упрощённого синтаксиса объявления типа record. На помощь приходит специальный атрибут [property]:

-7