Вы на правильном пути. Модель представления представляет то, что нужно вашему представлению. Сущности отражают ваш домен, поэтому мост проецирует ваш домен на то, что нужно представлению.
В вашем примере, при возврате клиента вам нужен его применимый адрес на заданную дату. Например, по умолчанию используется текущая дата. Я предполагаю, что, поскольку дата ValidTo не имеет значения NULL, текущий адрес будет иметь дату ValidTo, установленную на что-то вроде DateTime.Max ()?
Что-то вроде этого должно указать вам в правильном направлении:
public CustomerViewModel GetCustomerAsAt(Guid customerId, DateTime? targetDate = null)
{
if (!targetDate.HasValue)
targetDate = DateTime.Today;
var customer = _context.Customers
.Where(c => c.CustomerId == customerId)
.Select(c => new {
{
c.CustomerId,
c.CustomerName,
Address = c.Address
.SingleOrDefault(a => a.ValidFromDt <= targetDate && a.ValidToDt > targetDate);
}).Single();
var viewModel = new CustomerViewModel
{
CustomerId = customer.CustomerId,
CustomerName = customer.CustomerName,
Country = customer.Address?.Country,
City = customer.Address?.City,
Street = customer.Address?.Street,
Postcode = customer.Address?.Postcode,
};
return viewModel;
}
Выражение Linq-to-EF можно настроить для прямого проецирования в модель представления, хотя я выбрал двухэтапный подход, чтобы сделать его более читабельным. Метод принимает необязательную дату, чтобы найти адрес, который применялся в эту дату, по умолчанию используется сегодняшняя дата, если она не была предоставлена. Метод вызовет исключение, если идентификатор клиента недействителен, но вернет пустые данные адреса, если у клиента не было данных адреса, охватывающего желаемую дату. Он будет сгенерирован, если несколько адресов охватывают дату.
«этот процесс кажется очень неэффективным»
Это стоимость разделения проблем между данными / доменом и тем, что нужно представлению. Хотя это может показаться большим количеством кода, он дает вам полный контроль над размером полезной нагрузки, перемещаемой между сервером и клиентом. В большинстве случаев, когда проекция остается довольно простой, вы можете использовать Automapper и его метод ProjectTo
для управления отображением. Тогда это буквально просто один оператор в цепочке с .ProjectTo<CustomerViewModel>(mapperConfig)
. Вероятно, можно подключить ваш пример / w Automapper, хотя я обычно использую ProjectTo для очень простых (хотя, возможно, более крупных объектов), например, при отображении подробного представления объекта в виде графика модели представления.
Для сообщения обратно на сервер, я предпочитаю использовать более DDD подход с использованием методов мутатора для изменения сущностей, а не publi c Setters. Таким образом, мои методы контроллера по расширению могут передавать ViewModels с применимыми данными, или они просто передают значения параметров, которые я sh обновляю, если их недостаточно для обоснования модели представления. Я редко когда-либо передаю модель представления, которая была предоставлена для представления, обратно на сервер, даже несмотря на то, что такие технологии, как MVC, были настроены по умолчанию. Мое представление часто состоит из нескольких небольших частичных моделей представления, извлекаемых по запросу, с отправкой одного или нескольких действий взамен.
Действия предназначены для передачи только данных, необходимых для выполнения обновления. Я бы никогда не согласился с чем-то вроде «UpdateCustomer», передавая безусловно всю модель CustomerViewModel / Entity со структурой истории адресов для копирования поверх строки данных моего клиента. Если есть действие по обновлению адреса клиента, я бы ожидал, что будет метод с именем UpdateCustomerAddress(customerAddressViewModel)
, передающий только те поля, которые могут быть обновлены, с моим бизнес-логом c, подтверждающим это и вызывающим customer.UpdateAddress()
для обработки обеспечения исторической характер моей истории адресов управляется правильно. Он может быть более подробным в коде, но он прост по назначению и уменьшает побочные эффекты и эксплуатацию.