我现在正在学习ASP.NET MVC一段时间了。我遵循在互联网或书籍中找到的一些指导原则,我希望确保我遵循关于视图模型模式的开发中的良好实践。以下是博客实施的简单示例。能否请你确认我是在正确的方式。假设我想在视图上显示帖子标题+描述,并在此帖子上显示评论。所以我有一个关于它的2个部分视图的视图。
在我的控制器中:
public ActionResult DetailPost(int postID)
{
// retrieve infos
Post postModel = repository.Posts.FirstOrDefault(p => p.PostID == postID);
IEnumerable<Comments> commentsModel = postModel.Comments;
// prepare view model
DetailPostViewModel detailPostViewModel = new DetailPostViewModel
{
Post = postModel,
Comments = commentsModel,
};
return View(detailPostViewModel);
}
所以在这里,我会看到一个由两件事组成的视图模型:
视图模型将传递给视图以跟随视图模型模式。
在我的详情视图中
@model WebUI.ViewModels.DetailPostViewModel
@{
ViewBag.Title = "Detail";
}
<h2>Detail</h2>
@Html.Partial("_DetailPost", Model.Post)
@Html.Partial("_Comments", Model.Comments)
所以在这里,我使用了我的DetailPostViewModel。对于我的部分观点,我传递了必要的信息,即实体!?我不知道这是正确的方式,还是我必须在这里通过视图模型呢?
在我的_DetailPost视图中
@model Domain.Entities.Post
@Html.DisplayForModel()
...
所以在我的部分视图中,我使用en实体作为模型。是好还是坏?
在我的_Comments视图中
@model IEnumerable<Domain.Entities.Comment>
@foreach (var item in Model)
{
@Html.DisplayFor(Model => item.Title)
@Html.DisplayFor(Model => item.Username)
}
所以在我的部分视图中,我使用en实体作为模型。是好还是坏?
感谢您的建议/建议。
修改
我尝试在Automapper的帮助下实现我的视图模型模式,但是我遇到了一些问题,如下所述。我理解将一个对象映射到另一个对象的原理。在基本情况下,它运作良好。现在,让我们看一个更复杂的情况:我有一个由一些属性(id,title,...)组成的帖子,每个帖子可以附加一些注释(对象),每个注释只能附加一个用户(对象) 。我希望得到一个包含我所有帖子的视图模型,并获取所有评论,并为每个评论获得用户附加。
以下是域名实体:
public class Post
{
public int PostID { get; set; }
public string Title { get; set; }
...
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string CommentText { get; set; }
...
public virtual <User> User { get; set; }
}
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
...
}
以下是视图模型:
public class PostVM
{
public int PostID { get; set; }
public string Title { get; set; }
...
public virtual ICollection<CommentVM> CommentsVM { get; set; }
}
public class CommentVM
{
public int CommentID { get; set; }
public string CommentText { get; set; }
...
public virtual <UserVM> UserVM { get; set; }
}
public class UserVM
{
public int UserID { get; set; }
public string Username { get; set; }
...
}
这是我的控制器:
// I would like to list all posts in the DB.
public ViewResult Index()
{
PostVM viewModel = new PostVM
{
...
}
return View(viewModel);
}
如何将我的(实体)帖子对象列表映射到我的视图模型对象和所有子对象(注释,用户)?
谢谢!
答案 0 :(得分:5)
你很接近,但对我而言,这不是视图模型的正确用法。您有以下内容:
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
和(~/Views/Shared/DisplayTemplates/CommentViewModel.cshtml
):
@model WebUI.ViewModels.CommentViewModel
@Html.DisplayFor(x => x.Title)
@Html.DisplayFor(x => x.Username)
显然,由于Comments是一个枚举,因此将自动为集合的每个元素呈现显示模板。这样您就不需要在视图中编写任何循环。
就控制器逻辑而言,我个人喜欢使用AutoMapper在域实体和视图模型之间进行映射。
所以看起来像这样:
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
和Comments
。 AutoMapper依赖于标准命名约定,您也可以编写自己的自定义约定策略,以避免单独映射每个成员。然后,您只需要在定义视图模型时保持一致并遵循惯例。
然后在控制器中:
IEnumerable<Post> posts = ...
IEnumerable<PostVm> postVms = Mapper.Map<IEnumerable<Post>, IEnumerable<PostVM>>(posts);
return View(postVms);
答案 1 :(得分:2)
您确实将自己的域模型用于视图中。您的视图模型只是域模型的容器。
就我个人而言,在我的视图中使用实体或域模型没有任何问题。我通常从他们开始。当域/实体模型没有给我我想要的东西时(而不是在我的视图中引入逻辑),我切换到查看模型。
没有其他代码依赖于您的视图,因此可以轻松地重构它们并随时切换模型。