Показать только связанную сущность в ASP. NET MVC с использованием Entity Framework - PullRequest
1 голос
/ 04 августа 2020

Я новичок в MVC и работаю над личным проектом, используя его в сочетании с EntityFramework. Я начинаю с существующей базы данных, где у меня есть класс Customer с некоторыми связанными сущностями, одна из них - Address.

Так, например:

public partial class Customer
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public Customer
        {            
            this.Address = new HashSet<Address>();
        }
    
        public System.Guid CustomerId { get; set; }   
        public string CustomerName { get; set; }        
        public string CustomerCountry { get; set; }
        
        public virtual ICollection<Address> Address { get; set; }

Тогда у меня есть:

 public partial class Address
    {
        public System.Guid AddressId { get; set; }
        public System.Guid CustomerFk { get; set; }
        public string City{ get; set; }
        public string Street{ get; set; }
        public string PostalCode{ get; set; }            
        public System.DateTime ValidFromDt { get; set; }
        public System.DateTime ValidToDt { get; set; }            
    
        public virtual Customer Customer { get; set; }
    }
}

Итак, в этом сценарии у клиента может быть несколько адресов, и фактический адрес считается действительным на основе полей ValidFromDt и ValidToDt. Я хочу показать в представлении таблицу с некоторыми клиентами и их адресами, но я борюсь, потому что не знаю, как правильно отобразить в одном представлении данные клиента и данные действительного только адрес. Моя цель - показать / работать с одним экземпляром связанной сущности.

В моем первом подходе я использовал ViewModel:

public class CustomerListViewModel
    {       

        public Guid CustomerID { get; set; }        

        [Display(Name = "Name")]
        public string CustomerName { get; set; }
        
        [Display(Name = "Country")]
        public string CustomerCountry { get; set; }       


        [Display(Name = "City")]
        public string City { get; set; }

        [Display(Name = "Street")]
        public string Street { get; set; }

        [Display(Name = "Postal code")]
        public string PostalCode { get; set; }

    }
}

Но я заметил, что обратно в CustomerController мне нужно запросить список клиентов, затем для каждого из них я должен извлечь единственный адрес на основе условия действительности, а затем мне нужно сопоставить эти поля с ViewModel, и этот процесс кажется очень неэффективным (также с учетом того, что, возможно, мне следует сделать это для другого действия, когда я редактирую данные клиента).

Есть ли более эффективный способ продолжить? Правильно ли, например, запрашивать один адрес непосредственно в View с помощью синтаксиса Razor? Следует ли мне использовать ViewModel, построенный на комбинации нескольких ViewModels?

Вероятно, мне все еще не хватает чего-то EF.

Спасибо всем.

1 Ответ

0 голосов
/ 05 августа 2020

Вы на правильном пути. Модель представления представляет то, что нужно вашему представлению. Сущности отражают ваш домен, поэтому мост проецирует ваш домен на то, что нужно представлению.

В вашем примере, при возврате клиента вам нужен его применимый адрес на заданную дату. Например, по умолчанию используется текущая дата. Я предполагаю, что, поскольку дата 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() для обработки обеспечения исторической характер моей истории адресов управляется правильно. Он может быть более подробным в коде, но он прост по назначению и уменьшает побочные эффекты и эксплуатацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...