AutoMapper в сложных представлениях (многократное зацикливание) - PullRequest
1 голос
/ 17 декабря 2011

Раньше сегодня один полезный человек (здесь, в StackOverflow) указал мне на AutoMapper , я его проверил, и он мне очень понравился!Однако теперь я немного застрял.

В моем приложении Code First MVC3 в моем [Home / Index] мне нужно отобразить следующую информацию из моих сущностей:

  1. СписокСообщения [int Id, строка Body, int Likes, строка p.User.FirstName, строка p.User.LastName]
  2. Список тегов [int Id, имя строки]
  3. Список всехАвторы, которые существуют в моей базе данных [string UrlFriendlyName]

До сих пор мне удалось управлять только точкой 1 в списке, выполнив следующие действия для моей индексной модели представления:

public class IndexVM
{
    public int Id { get; set; }
    public string Body { get; set; }
    public int Likes { get; set; }
    public string UserFirstName { get; set; }
    public string UserLastName { get; set; }
}

И наДомашний контроллер, Index ActionMethod, который у меня есть:

public ActionResult Index()
{
    var Posts = postsRepository.Posts.ToList();
    Mapper.CreateMap<Post, IndexVM>();
    var IndexModel = Mapper.Map<List<Post>, List<IndexVM>>(Posts);
    return View(IndexModel);
}

Наконец, на моем View у меня есть строгая типизация:

@model IEnumerable<BlogWeb.ViewModels.IndexVM>

И я передаю каждый элемент в IndexVM IEnumberableЧастичный просмотр через:

@foreach (var item in Model)
{
    @Html.Partial("_PostDetails", item)
}

У меня вопрос, как мне также достичь пунктов 2 и 3, не нарушая того, чего я достиг в пункте 1.

Я попытался поместить материалВ настоящее время у меня есть для IndexVM в подклассе, и свойство списка на tКласс для родителей, но он не работал.

Я был бы очень признателен за любую помощь .

Спасибо.

Ответы [ 2 ]

2 голосов
/ 17 декабря 2011

Из книги ASP.NET MVC2 In Action:

Некоторые экраны более сложны, чем одна таблица.Они могут содержать несколько таблиц и дополнительные поля других данных: изображения, заголовки, промежуточные итоги, графики, диаграммы и миллионы других вещей, которые усложняют представление.Решение для презентации модели масштабируется, чтобы справиться со всеми.Разработчики могут уверенно поддерживать даже самые мрачные экраны, если модель презентации разработана хорошо.Если экран содержит несколько сложных элементов, модель представления может быть оболочкой, объединяя их все и освобождая файл разметки от большой сложности.Хорошая модель представления не скрывает эту сложность - она ​​представляет ее точно и максимально просто, и она отделяет данные на экране от дисплея.

Создайте модель представления, которая представляет ваш экран.Затем создайте его и передайте в View. Эта книга великолепна и рассказывает об использовании модели презентации.С AutoMapper подумайте о том, как вы могли бы выполнить свое отображение без него, а затем используйте его.AutoMapper не собирается делать что-либо волшебное, он устраняет шлепки клавиатуры.

AutoMapper, оставьте ваш список требований:

  1. Список сообщений [int Id, string Body, intLikes, строка p.User.FirstName, строка p.User.LastName]
  2. Список тегов [int Id, имя строки]
  3. Список всех авторов, существующих в моей базе данных [string UrlFriendlyName]

и при условии, что у вас есть следующие модели: Post, Tag, Author

Лично мне не нравится передавать объекты Model в мою презентацию в MVC или MVVM, но это я.Скажем, мы следуем этому здесь и создаем PostDisplay, TagDisplay и AuthorDisplay.

На основании требований View, ViewModel будет выглядеть следующим образом:

Public class IndexVM
{
    Public List<PostDisplay> Posts {get; set;}
    Public List<TagDisplay> Tags {get; set;}
    Public List<AuthorDisplay> Authors {get; set;}
}

В этом случае способ составления Viewпотребует от вас его построения:

public ActionResult Index()
{
    var posts = postsRepository.Posts.ToList();
    var tags = postsRepository.Tags.ToList();
    var authors = postsRepository.Authors.ToList();

    Mapper.CreateMap<Post, PostDisplay>();
    Mapper.CreateMap<Tag, TagDisplay>();
    Mapper.CreateMap<Author, AuthorDisplay>();

    private var IndexVM = new IndexVM
    {
        Posts = Mapper.Map<List<Post>, List<PostDisplay>>(posts),
        Tags = Mapper.Map<List<Tag>, List<TagDisplay>>(tags),
        Authors = Mapper.Map<List<Author>, List<AuthorDisplay>>(authors)
    };

    return View(IndexVM);
}

Итак, в итоге вы получите ViewModel для передачи вашему представлению, которое точно соответствует тому, что вы хотите отобразить, и не тесно связано с вашей моделью домена.,Я не могу придумать, как AutoMapper отобразит три отдельных списка результатов в один объект.

Чтобы уточнить, AutoMapper отобразит дочерние коллекции так, что структура, такая как:

public class OrderItemDto{}
public class OrderDto
{
    public List<OrderItemDto> OrderItems { get; set; }
}

, будет отображатьсяto:

public class OrderItem{}
public class Order
{
    public List<OrderItem> OrderItems { get; set; }
}

Пока вы говорите, как сопоставлять типы: OrderDto -> Order и OrderItemDto -> OrderItem.

1 голос
/ 17 декабря 2011

В качестве альтернативы включению всех ваших списков сущностей в одну модель представления вы можете использовать @ Html.Action.Затем в представлении экрана:

@Html.Action("Index", "Posts")
@Html.Action("Index", "Tags")
@Html.Action("Index", "Authors")

Таким образом, вашему представлению / модели «Индекс / экран» не нужно знать о других моделях представления.Частицы доставляются отдельными дочерними методами действий на отдельных контроллерах.

Все вещи автоматического автомата все еще применяются, но вы все равно сопоставили бы свои сущности с моделями представления индивидуально.Разница в том, что вместо сопоставления в HomeController.Index () вы должны сделать это в PostsController.Index (), TagsController.Index () и AuthorsController.Index ().

Responseкомментировать 1

public class IndexVM
{
    // need not implement anything for Posts, Tags, or Authors
}

Затем реализовать 3 разных метода на 3 разных контроллерах.Вот один пример для PostsController.Следуйте одному и тому же шаблону для TagsController и AuthorsController

// on PostsController
public PartialViewResult Index()
{
    var posts = postsRepository.Posts.ToList();

    // as mentioned, should do this in bootstrapper, not action method
    Mapper.CreateMap<Post, PostModel>();

    // automapper2 doesn't need source type in generic args
    var postModels = Mapper.Map<List<PostModel>>(posts); 
    return PartialView(postModels);
}

Для этого вам потребуется создать соответствующее частичное представление, строго типизированное как @model IEnumerable<BlogWeb.ViewModels.PostModel>.В этом представлении поместите HTML-код, отображающий пользовательский интерфейс «Посты» (перейдите из представления HomeController.Index).

На вашем HomeController просто сделайте следующее:

public ActionResult Index()
{
    return View(new IndexVM);
}

Держите ваш взгляд строго набранным на IndexVM

@model IEnumerable<BlogWeb.ViewModels.IndexVM>

... и затем получите сообщения,Теги и авторы, например, так:

@Html.Action("Index", "Posts")

Ответ на комментарий 2

Начальная загрузка ... Ваши конфигурации Mapper.CreateMap должны выполняться только один раз для каждого домена приложения.Это означает, что вы должны выполнять все ваши вызовы CreateMap из Application_Start.Помещение их в код контроллера просто создает ненужные накладные расходы.Конечно, карты должны быть созданы - но не во время каждого запроса.

Это также помогает при модульном тестировании.Если вы поместите все свои вызовы Mapper.CreateMap в один статический метод, вы можете вызвать этот метод из метода модульного теста, а также из Global.asax Application_Start.Затем в модульном тесте один метод может проверить, что ваши вызовы CreateMap установлены правильно:

AutoMapperBootStrapper.CreateAllMaps();
Mapper.AssertConfigurationIsValid();
...