Asp.net mvc 3 复杂视图上的自动映射(多重循环)

Asp.net mvc 3 复杂视图上的自动映射(多重循环),asp.net-mvc-3,entity-framework-4.1,ef-code-first,automapper,Asp.net Mvc 3,Entity Framework 4.1,Ef Code First,Automapper,今天早些时候,一位乐于助人的人(这里是Stack Overflow)向我指出,我查看了它,我非常喜欢它!但是现在我有点被卡住了 在我的代码第一个MVC3应用程序中,在我的[Home/Index]上,我需要显示来自实体的以下信息: 帖子列表[int-Id,string-Body,int-Likes,string p.User.FirstName,string p.User.LastName] 标记列表[整数Id,字符串名称] 我的数据库中存在的所有作者的列表[string UrlFriendlyN

今天早些时候,一位乐于助人的人(这里是Stack Overflow)向我指出,我查看了它,我非常喜欢它!但是现在我有点被卡住了

在我的代码第一个MVC3应用程序中,在我的[Home/Index]上,我需要显示来自实体的以下信息:

  • 帖子列表[int-Id,string-Body,int-Likes,string p.User.FirstName,string p.User.LastName]
  • 标记列表[整数Id,字符串名称]
  • 我的数据库中存在的所有作者的列表[string UrlFriendlyName]
  • 到目前为止,我仅通过对我的Index ViewModel执行以下操作来管理列表中的第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; }
    }
    
    在Home Controller上,我有索引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);
    }
    
    我的问题是,我如何既能达到第2点和第3点,又不打破第1点的成绩


    我尝试将目前用于IndexVM的内容放入一个子类,并在父类上具有List属性,但没有成功。

    来自ASP.NET MVC2 In Action手册:

    有些屏幕比单个表更复杂。它们可能具有 多个表和其他数据的附加字段:图像、标题、, 小计,图表,图表,以及其他让事情复杂化的东西 景色。表示模型解决方案可以扩展以处理所有这些问题。 开发人员可以自信地维护最粗糙的屏幕,只要 因为演示模型设计得很好。如果屏幕确实包含 多个复杂元素,表示模型可以是包装器, 将它们全部组合起来并减轻标记文件的复杂性。A. 好的表示模型并不会掩盖这种复杂性,它代表了这种复杂性 准确且尽可能简单,它可以在 从显示屏上选择屏幕

    制作一个表示屏幕的ViewModel。然后构建它并将其传递给视图。这本书很好,它谈到了如何使用演示模型。使用AutoMapper,想想没有它您将如何完成映射,然后利用它。AutoMapper不会做任何魔术,它消除了键盘敲击

    将AutoMapper放在一边,列出您的需求:

  • 帖子列表[int-Id,string-Body,int-Likes,string p.User.FirstName,string p.User.LastName]
  • 标记列表[整数Id,字符串名称]
  • 我的数据库中存在的所有作者的列表[string] UrlFriendlyName]
  • 假设您有这些模型实体:Post、Tag、Author

    就我个人而言,我不喜欢在MVC或MVVM中将模型实体传递给我的演示文稿,但我就是这样。假设我们在这里遵循这一点,创建PostDisplay、TagDisplay和AuthorDisplay

    根据视图的要求,ViewModel将如下所示:

    Public class IndexVM
    {
        Public List<PostDisplay> Posts {get; set;}
        Public List<TagDisplay> Tags {get; set;}
        Public List<AuthorDisplay> Authors {get; set;}
    }
    
    公共类IndexVM
    {
    公共列表发布{get;set;}
    公共列表标记{get;set;}
    公共列表作者{get;set;}
    }
    
    在这种情况下,视图的组成方式将要求您构建它:

    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);
    }
    
    public ActionResult Index()
    {
    var posts=postsRepository.posts.ToList();
    var tags=postsRepository.tags.ToList();
    var authors=postsRepository.authors.ToList();
    CreateMap();
    CreateMap();
    CreateMap();
    私有变量IndexVM=新的IndexVM
    {
    Posts=Mapper.Map(Posts),
    Tags=Mapper.Map(标记),
    Authors=Mapper.Map(作者)
    };
    返回视图(IndexVM);
    }
    
    因此,您最终得到的是一个要传递到视图的ViewModel,它准确地表示您想要显示的内容,并且不与域模型紧密耦合。我想不出让AutoMapper将三个单独的结果列表映射到一个对象的方法

    为了澄清这一点,AutoMapper将映射子集合,使其具有如下结构:

    public class OrderItemDto{}
    public class OrderDto
    {
        public List<OrderItemDto> OrderItems { get; set; }
    }
    
    公共类OrderItemDto{}
    公共类OrderDto
    {
    公共列表OrderItems{get;set;}
    }
    
    将映射到:

    public class OrderItem{}
    public class Order
    {
        public List<OrderItem> OrderItems { get; set; }
    }
    
    公共类OrderItem{}
    公共阶级秩序
    {
    公共列表OrderItems{get;set;}
    }
    

    只要您告诉它如何映射类型:OrderDto->Order和OrderItemDto->OrderItem。

    作为在单个viewmodel中包含所有实体列表的替代方法,您可以使用@Html.Action。然后,在屏幕视图中:

    @Html.Action("Index", "Posts")
    @Html.Action("Index", "Tags")
    @Html.Action("Index", "Authors")
    
    这样,您的索引/屏幕视图和模型就不需要了解其他视图模型。部分由单独的子操作方法在单独的控制器上传递

    所有automapper的内容仍然适用,但您仍然可以将实体单独映射到viewmodels。不同之处在于,您可以在PostsController.Index()、TagsController.Index()和AuthorsController.Index()中进行映射,而不是在HomeController.Index()中进行映射

    对评论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);
    }
    
    在IndexVM上保持视图的强类型

    @model IEnumerable<BlogWeb.ViewModels.IndexVM>
    
    对评论2的回应

    自举。。。您的Mapper.CreateMap配置只需在每个应用程序域中进行一次。这意味着您应该从应用程序开始执行所有CreateMap调用。将它们放在控制器代码中只会产生不必要的开销。当然,需要创建映射,但不是在每个请求期间

    这也有助于单元测试。如果将所有Mapper.CreateMap调用放在一个静态方法中,则可以从单元测试方法以及Global.asax应用程序_Start调用该方法。然后在单元测试中,有一种方法可以测试CreateMap调用是否设置正确:

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

    谢谢你的回复,德里克。我已经照你的建议去做了。但我现在不知道如何继续与汽车制造商。我如何告诉AutoMapper以我的ViewModel现在的方式进行操作?谢谢,还有,看看这本书,它有一个很好的章节是关于演示模型的。只是想让你知道,我不小心投了反对票,而不是投了赞成票。
    public ActionResult Index()
    {
        return View(new IndexVM);
    }
    
    @model IEnumerable<BlogWeb.ViewModels.IndexVM>
    
    @Html.Action("Index", "Posts")
    
    AutoMapperBootStrapper.CreateAllMaps();
    Mapper.AssertConfigurationIsValid();