Введение
Проще говоря, предположим, что у нас есть 3 класса в предметной области (я называю их бизнес-объектами, BO). Я не учитываю конструкторы и прочее, более или менее важны только свойства.
публичный класс VehicleBo { общедоступная строка Производитель { get; набор; } } общественный класс CarBo: VehicleBo { общественный ИНТ PassengerCount {получить; набор; } } общественный класс TruckBo: VehicleBo { общественный двойной MaximumLoad {get; набор; } общественный bool HasTrailer {получить; набор; } } Тогда у нас есть только один объект DTO, который «агрегирует» все свойства из иерархии BO.
публичный класс VehicleDto { общедоступная строка Производитель { get; набор; } общественный ИНТ PassengerCount {получить; набор; } общественный двойной MaximumLoad {get; набор; } общественный bool HasTrailer {получить; набор; } // Соответствует дискриминатору, определенному в VehicleBo при сопоставлении BO с базой данных с помощью EF Core. общественная строка VehicleType {get; набор; } } Мы используем Entity Framework Core для сопоставления наших BO с таблицами в реляционной базе данных. Существует только одна таблица (Транспортные средства) для всех BO, т.е. VehicleBo, CarBo, TruckBo. Таблица Vehicles сопоставляется с сущностью VehicleBo, а остальные BO хранятся в Vehicles с использованием стратегии «Таблица по иерархии» (TPH).< /п> За кулисами Теперь я ищу хорошее и последовательное решение по этому поводу:
Когда пользователь просматривает содержимое таблицы Vehicles, он видит данные, хранящиеся в объекте DTO (VehicleDto). Для целей фильтрации и сортировки существует крошечная «структура», которая этим управляет. Он пытается «сопоставить» свойство DTO с соответствующим свойством BO, но очень простым способом — имена должны совпадать.
Например, для фильтра наш FE отправляет запрос (в символьном коде):
{ Тип: Транспортное средствоDto, Предикат: v => v.Производитель == "Форд" } Фреймворк преобразует это примерно так:
{ Тип: Транспортное средствоБо, Предикат: v => v.Производитель == "Форд" } (Обратите внимание на VehicleBo вместо VehicleDto) И он отправляет его в BE.
Здесь все просто, поскольку свойство Manufacturer находится как в DTO, так и в BO.
Однако это не относится, например, к PassengerCount.
FE отправляет:
{ Тип: Транспортное средствоDto, Предикат: v => v.PassengerCount > 2 } Фреймворк не может преобразовать это в:
{ Тип: Транспортное средствоБо, Предикат: v => v.PassengerCount > 2 } Поскольку свойство PassengerCount не существует в VehicleBo.
Поэтому на наш BE затем отправляется следующее:
{ Тип: Транспортное средствоБо, Предикат: v => ((CarBo) v).PassengerCount > 2 } Это нормально, EF прекрасно может преобразовать это в запрос SQL.
Вызов
Но я ищу элегантный способ дать нашей структуре подсказку относительно сопоставления «DTO-to-BO».
Первая идея — использование атрибутов.
публичный класс PropertyMappingAttribute : Атрибут { public Expression PropertySelector {get; } public PropertyMappingAttribute (Expression propertySelector) { PropertySelector = PropertySelector; } } // ... общественный класс VehicleDto { общедоступная строка Производитель { get; набор; } [PropertyMapping(v => ((CarBo) v).PassengerCount)] общественный ИНТ PassengerCount {получить; набор; } общественный двойной MaximumLoad {get; набор; } общественный bool HasTrailer {получить; набор; } // Соответствует дискриминатору, определенному в VehicleBo при сопоставлении BO с базой данных с помощью EF Core. общественная строка VehicleType {get; набор; } } К сожалению, в C# в настоящее время это невозможно. Для параметров атрибута мы должны использовать примитивные типы, такие как строка:
публичный класс PropertyMappingAttribute: Атрибут { общественная строка PropertySelector {get; } public PropertyMappingAttribute (строка propertySelector) { PropertySelector = PropertySelector; } } // ... общественный класс VehicleDto { общедоступная строка Производитель { get; набор; } [PropertyMapping("((CarBo) v).PassengerCount")] общественный ИНТ PassengerCount {получить; набор; } общественный двойной MaximumLoad {get; набор; } общественный bool HasTrailer {получить; набор; } // Соответствует дискриминатору, определенному в VehicleBo при сопоставлении BO с базой данных с помощью EF Core. общественная строка VehicleType {get; набор; } } Но последний подход неудобен и подвержен ошибкам.
Есть ли у вас предложения, как решить эту проблему элегантно, сохраняя при этом простоту и безопасность ввода?