Шаблоны для отображения данных между моделями домена
Это обычная вещь, которую мне нужно делать в последнее время, и я искал любые общие шаблоны, чтобы сделать это немного легче.
Основной смысл всего этого в том, что у меня есть некоторые модели данных, которые смоделированы для удовлетворения ORM и просто выполняют операции CRUD для объектов. Эти модели в настоящее время открыты через репозитории/фабрики (в зависимости от того, являются ли их C или RUD).
Затем у меня есть модель представления, которая немного читаема и посыпана проблемами пользовательского интерфейса, такими как данные проверки и сопоставления между представлением (это сценарий ASP.MVC, но эта ситуация может быть абстрагирована в большинстве ситуаций).
Итак, скажем, я перехожу к localhost/user/1, который должен пойти и получить мне пользователя с Id 1 в БД, а затем отобразить его в пользовательском интерфейсе. В конечном итоге это должно вывести данные из области данных, а затем отобразить их в модели ui для показа.
Вот пример сценария:
public class OrmUser
{
public int Id {get;set;}
public string Name {get;set;}
public IList<Permission> Permissions {get;set;}
}
public class UiUser
{
[Required]
public int Id {get;set;}
[Required]
public string Name {get;set;}
public bool IsUserAdmin {get;set;}
}
public class UserMapper : IMapper<UiUser>
{
public UiUser Get(int id)
{
var ormUser = UserRepository.Get(id);
var uiUser = new UiUser
{
Id = ormUser.Id,
Name = ormUser.Name,
IsUserAdmin = IsUserAdmin(ormUser.Permissions)
}
}
private bool IsUserAdmin(IList<Permission> permissions)
{
return permissions.SomeLinq(ToFindIfTheyAreAnAdmin);
}
}
Это простой пример, но показывает, как модель данных содержит много одинаковой информации, но в этом вопросе вы не заботитесь обо всей информации, просто ее подмножестве. Таким образом, имея сопоставитель, вы можете абстрагировать не только отображение, но и связь с доменом данных, однако вам нужно написать класс сопоставления для каждого типа, и вышеупомянутое предполагает, что это одностороннее сопоставление, а не одностороннее отображение, которое потребуется еще немного кода.
Итак, как вы все собираетесь выполнять это сопоставление? Поскольку в настоящее время я только что писал матчи абстракции, которые в основном позволяют слою пользовательского интерфейса запускать запрос и возвращать модель представления, абстрагировать репозитории и копировать данные из одной модели в другую, и она просто чувствует, что должно быть лучше способ сделать это.
Ответы
Ответ 1
В .net вы должны, вероятно, взглянуть на Automapper
https://github.com/AutoMapper/AutoMapper
Это, по сути, позволяет:
CreateMap<Domain.Customer, ViewModel.Customer>()
И вы можете затем сопоставить между ними, сказав:
var vmCustomer = Mapper.Map<Domain.Customer, ViewModel.Customer>(domainCustomer);
Скорее всего, это сэкономит вам тонну кода плиты котла.
Ответ 2
Да. AutoMapper - это инструмент для вас. Есть тонна статьи об этом, но идите к источнику - Джимми Богарду. Он мастерский ум за AutoMapper.
Посмотрите на эту статью в блоге http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
В настоящее время AutoMapper поддерживает MVC очень эффективным способом, при котором вы можете украсить свои классы атрибутами, на которых должно выполняться сопоставление.
/С наилучшими пожеланиями Магнус
Ответ 3
В этом конкретном случае я мог бы просто написать метод расширения для пользователя на моем уровне представления.
public static class UserPresentationExtensions{
public bool IsAdmin(this User user){
return permissions.SomeLinq(ToFindIfTheyAreAnAdmin);
}
}
Вид будет выглядеть примерно так:
@model User
<fieldset>
<legend>User: @Model.Name</legend>
@if(user.IsAdmin()){
User is an admin
}
</fieldset>
Чтобы избежать неоднократного импорта пространства имен, для этого используйте web.config:
<configuration>
<system.web.webPages.razor>
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="MyProject.PresentationExtensions" />
...
У вас есть точка, однако, скажем, вы хотите добавить валидацию к вашим моделям, поэтому вы решили добавить атрибут [Обязательный] к свойству Name. Хотя тогда, если вы используете общую модель, то ваш уровень данных должен знать об аннотации, и если ваш уровень данных должен иметь некоторые атрибуты, ваш уровень пользовательского интерфейса должен знать о них. В самых простых ситуациях вы правы, иногда проще просто иметь 1 модель, которая является правдой, и просто получить доступ к ней по-другому, но для большинства сложных проектов вы столкнетесь с обеими сторонами, чтобы попытаться сохранить несколько строк кода.
Я сказал - в этом конкретном случае. Этот подход применяется только в том случае, если сопоставление действительно не стоит, и есть только однонаправленная связь (вам просто нужно ее отобразить).
Когда дело доходит до получения сообщений, оно становится немного другим. В большинстве случаев подход "один размер подходит всем" будет применять так называемый принцип Thunderdome. То есть:
Все методы контроллера берут в один объект ViewModel (или в некоторых случаях нулевые объекты) и возвращают один объект ViewModel (один объект входит, один объект уходит).
Однако нередко я предпочитаю использовать метод extension/html helper и просто передавать аргументы в действие следующим образом:
public void BatheCat(int id /* cat id */, int bathId, string shampoo){
...
}
Если счетчик параметров выходит из-под контроля (я не беспокоюсь, когда он равен <= 3), я просто инкапсулирую
они (вот пример из моего проекта).