你很接近,但对我来说这不是视图模型的正确用法。您有以下内容:
public class DetailPostViewModel
{
public Post Post { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
这是某种混合伪视图模型。真实的视图模型不应引用任何域模型。
更正确的例子如下:
public class DetailPostViewModel
{
public PostViewModel Post { get; set; }
public IEnumerable<CommentViewModel> Comments { get; set; }
}
当然,PostViewModel 和 CommentViewModel 是其各自领域实体的相应视图模型。
另外,我将使用显示模板来代替部分:
@model WebUI.ViewModels.DetailPostViewModel
@{
ViewBag.Title = "Detail";
}
<h2>Detail</h2>
@Html.DisplayFor(x => x.Post)
@Html.DisplayFor(x => x.Comments)
然后您将拥有各自的显示模板(~/Views/Shared/DisplayTemplates/PostViewModel.cshtml
):
@model WebUI.ViewModels.PostViewModel
... some properties of a post
and (~/Views/Shared/DisplayTemplates/CommentViewModel.cshtml
):
@model WebUI.ViewModels.CommentViewModel
@Html.DisplayFor(x => x.Title)
@Html.DisplayFor(x => x.Username)
显然,由于 Comments 是一个枚举,因此将自动为集合中的每个元素呈现显示模板。这样您就不需要在视图中编写任何循环。
就控制器逻辑而言,我个人喜欢使用自动映射器 http://automapper.org/在域实体和视图模型之间进行映射。
所以它可能看起来像这样:
public ActionResult DetailPost(int postID)
{
// retrieve infos
var postModel = repository.Posts.FirstOrDefault(p => p.PostID == postID);
var viewModel = new DetailPostViewModel
{
Post = Mapper.Map<Post, PostViewModel>(postModel),
Comments = Mapper.Map<IEnumerable<Comment>, IEnumerable<CommentViewModel>>(postModel.Comments)
};
return View(viewModel);
}
或者甚至更好:
public class PostViewModel
{
... some properties of a Post
public IEnumerable<CommentViewModel> Comments { get; set; }
}
进而:
public ActionResult DetailPost(int postID)
{
// retrieve infos
var postModel = repository.Posts.FirstOrDefault(p => p.PostID == postID);
var viewModel = Mapper.Map<Post, PostViewModel>(postModel);
return View(viewModel);
}
然后你的视图将被强类型化为 PostViewModel:
@model WebUI.ViewModels.PostViewModel
@{
ViewBag.Title = "Detail";
}
<h2>Detail</h2>
... some properties of a post
@Html.DisplayFor(x => x.Comments)
根据评论部分的要求,域模型和视图模型之间的映射如下所示:
Mapper.CreateMap<User, UserVM>();
Mapper
.CreateMap<Comment, CommentVM>()
.ForMember(
dest => dest.UserVM,
opt => opt.MapFrom(src => src.User)
);
Mapper
.CreateMap<Post, PostVM>()
.ForMember(
dest => dest.CommentsVM,
opt => opt.MapFrom(src => src.Comments)
);
备注:两个ForMemeber
如果属性在视图模型中以相同的方式命名,则不需要调用:User
and Comments
。 AutoMapper 依赖于标准命名约定,您也可以编写自己的自定义约定策略以避免单独映射每个成员。那么您所需要的就是在定义视图模型时保持一致并遵循约定。
然后在控制器中:
IEnumerable<Post> posts = ...
IEnumerable<PostVm> postVms = Mapper.Map<IEnumerable<Post>, IEnumerable<PostVM>>(posts);
return View(postVms);