В C# 9 появилось новое ключевое слово record для обозначения неизменяемых (immutable) типов данных. Это значит, что вы не можете поменять свойство такого объекта без создания нового экземпляра. Классический пример подобного поведение — хорошо известный тип DateTime.
Вместе с ключевым словом добавился и упрощённый синтаксис по объявлению таких типов данных:
Объявленные таким сокращенным образом типы данных очень удобно использовать в качестве моделей для запросов и ответов в API. Например:
Здесь UserQuery представляет собой модель данных запроса, и в то же время при использовании паттерна CQRS эту же самую модель данных можно передать в обработчик (пример с использованием библиотеки MediatR будет ниже).
Модификация "немодифицируемого" типа record
Выше мы рассмотрели пример запроса (query), а теперь рассмотрим пример команды (command). Допустим, нам нужно сделать API для изменения имени пользователя по его id.
Команда на обновление пользователя будет выглядеть так:
А API, по которому мы хотим обновлять пользователя, будет выглядеть примерно так:
Т.е. получается, что мы должны собрать нашу команду из двух источников: id взять из пути запроса, а name из его тела. ASP.NET не поддерживает автоматический биндинг модели из разных источников, так что нам придется собирать её самостоятельно. Но как, если наша модель немодифицируемая?
С помощью ключевого слова with:
Пример реализации API для обновления имени пользователя с использованием MediatR и record в качестве команды
И ещё кое-что важное, если вы используете Swagger
Если вы используете Swagger, то в генерируемом им описании вашего API при таком подходе тело PUT запроса на обновление пользователя будет содержать поле id, дублирующее id из URL. Чтобы этого избежать, достаточно в вашей модели пометить поле id атрибутом JsonIgnore.
Но есть нюанс. Напрямую атрибут JsonIgnore нельзя применить к "параметру конструктора" при использовании упрощённого синтаксиса объявления типа record. На помощь приходит специальный атрибут [property]: