遵循视图模型模式的最佳实践

2024-02-06

我现在正在学习 ASP.NET MVC 一段时间。我遵循在互联网或书籍中找到的一些指南,并且我想确保在我的开发中遵循有关视图模型模式的良好实践。下面是一个博客实现的简单示例。您能否确认我走的路是正确的?假设我想在视图上显示帖子标题+描述,并在该帖子的评论下方显示。所以我有一个观点,其中有两个部分观点。

在我的控制器中:

    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()
...

因此,在我的部分视图中,我使用实体作为模型。是好还是坏?

在我的 _Comments 视图中:

@model IEnumerable<Domain.Entities.Comment>

@foreach (var item in Model)
{
    @Html.DisplayFor(Model => item.Title)
    @Html.DisplayFor(Model => item.Username)
}

因此,在我的部分视图中,我使用实体作为模型。是好还是坏?

感谢您的意见/建议。

EDIT

我尝试在 Automapper 的帮助下实现我的视图模型模式,但我遇到了一些问题,如下所述。我理解将一个对象映射到另一个对象的原理。在基本情况下它效果很好。现在,让我们看看更复杂的情况:我有一篇由一些属性(id、标题等)组成的帖子,并且每个帖子可以附加一些评论(对象)并且每个评论只能附加一个用户(对象) 。我想获得一个包含我所有帖子的视图模型,对于每一篇帖子,获取所有评论,并为每条评论附加用户。

以下是域实体:

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);
}

如何将(实体)帖子对象列表映射到我的视图模型对象和所有子对象(评论、用户)?

Thanks!


你很接近,但对我来说这不是视图模型的正确用法。您有以下内容:

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);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

遵循视图模型模式的最佳实践 的相关文章

随机推荐

  • 如何更改 pine 脚本中函数的全局变量?

    我正在尝试编写一个脚本来获得 9 个级别的江恩平方 我已经用其他语言完成了它 但无法理解此处的 pine 脚本 它说无法修改函数中的全局变量 有什么解决方案可以获取这里的值是我的脚本 version 4 study title Volume
  • Xcode 5.1 破坏了一些测试

    自从更新到 Xcode 5 1 以来 我在尝试运行单元测试 XCTestSuite 时遇到了错误 当我运行单独的测试方法时 出现错误Error while reading test scope bundle在控制台上没有结果 当我运行测试文
  • 如何在整个组织内共享数据

    组织在多个部门和应用程序之间共享关键数据有哪些好方法 举个例子 假设有一个主要应用程序和数据库来管理客户数据 组织中还有十个其他应用程序和数据库读取该数据并将其与自己的数据关联起来 目前 这种数据共享是通过数据库 DB 链接 物化视图 触发
  • 如何以最有效的方式将图像转换为字符串?

    我想将图像文件转换为字符串 以下作品 MemoryStream ms new MemoryStream Image1 Save ms ImageFormat Jpeg byte picture ms ToArray string formm
  • 如果索引列不同,则对一列的值求和?

    当索引列不同时 如何对一列中的值求和 最初 我有这样的 SQL 查询 SELECT COALESCE SUM ISNULL cast Quantity as int 0 0 AS QuantitySum FROM Records 也尝试这样
  • CDate 可以在没有 for 循环的 Range 上使用吗?

    我有一个列 其格式如下 Range A A NumberFormat dd mm yyyy 现在 我正在寻找一个有效的版本 For k 1 To Range A1 End xlDown Row from first to last row
  • 使用 zkcli 从 ZooKeeper 删除/删除 Solr 配置?

    我们在 ZK 中有一些旧的 Solr 配置 我们需要清理 删除 配置位于 configs AAA configs BBB 等 configs 下还有其他配置 Solr zkcli sh 脚本和文档没有提供删除配置的指导 唯一看起来明显的是
  • 获取当前活动 Xamarin 表单

    我使用 Leadtools SDK 进行条码阅读器 当我尝试获取当前活动时 它给我 null 这是我的代码 Stream resourceStream new MemoryStream Droid MainActivity activity
  • Thymeleaf:如何使用 Thymeleaf 在 JavaScript 中使用布尔运算符

    我正在使用 thymeleaf 在 javascript 中使用th inline javascript 但是当我们在 javascript thymeleaf 中添加布尔条件时 会抛出异常 如下所示 org xml sax SAXPars
  • 如何修复这个批处理文件? (打字机效果)

    我想让批处理脚本具有打字机风格 效果 同时删除文件并显示回声和其他内容 我希望它看起来像这样 Write1 echo Deleting Prefetches del s q C Windows Prefetch JAVAW pf echo
  • 在运行时根据请求参数创建EF Core DbContext

    背景 我们正在使用 ASP Net Core 和 Entity Framework Core 2 2 构建一个 Web 应用程序 我们正在连接到旧数据库 设置是有 16 个数据库 全部具有完全相同的架构 保存不同的上下文数据 我们无法改变这
  • Ctrl+M 使用 Sublime+Linux 运行 Makefile

    我目前正在考虑从 Pluma 以前的 gEdit 切换到 Sublime Text 到目前为止 我从 Sublime Text 中看到的内容非常好 I currently test Submlime Text 2 and would lik
  • 为什么 C 类型泛型表达式不能与 C++ 兼容?

    我似乎记得从一些可靠来源 即委员会成员在非官方渠道中发言 听到过模糊的评论 即 C 类型泛型表达式不会添加到 C 中 因为它们不能添加 据我所知 与 C 模板和重载相比 类型泛型表达式非常有限 但不存在需要定义为特殊情况的交互潜力 类型通用
  • 扩展实体

    我有一个名为 AbstractEntity 的类 它用 MappedSuperclass 进行注释 然后我有一个名为 User Entity 的类 它扩展了 AbstractEntity 这两个都存在于名为 foo bar framewor
  • 在向 COM 公开的 C# 对象上实现 ISupportErrorInfo

    我正在用 C 编写一个 COM 对象 我想使用它理解的机制 Err 对象 向 vba asp 客户端软件引发错误 在过去的好日子里 这意味着在 COM 对象上实现 ISupportErrorInfo 但我找不到任何有关如何在 C 对象中实现
  • 如何在selenium中按类名查找元素?

    在selenium中通过类名查找元素的语法是什么 请注意 我已经使用了以下语法 link elements driver find elements by class name BM30N 它给了我以下错误 C Users David De
  • iOS6大量下载超时

    看起来 iOS6 上使用 ASIHTTPRequest 的所有大型下载都会超时 有谁知道有任何 fork 已经为 iOS6 更新了这个库吗 我喜欢这个库并且真的不想切换 EDIT 此问题并非 ASIHTTPRequest 特有的 在测试 F
  • Django 1.2.4 CSRF验证失败

    当我执行 POST 表单时 Django 1 2 始终给我这个 CSRF 验证错误 我 认为 我已经完成了 Django 1 2 文档中要求的所有事情 即 确保 MIDDLEWARE CLASSES 包含在 django middlewar
  • SweetAlert确认对话框与asp.net listview删除?

    请帮我理解这一点 我创建了一个 ListView 显示来自 SQL 数据库的数据 我已经启用了插入 编辑和删除功能 一切正常 我想要什么 我想用SweetAlert http t4t5 github io sweetalert 提示用户确认
  • 遵循视图模型模式的最佳实践

    我现在正在学习 ASP NET MVC 一段时间 我遵循在互联网或书籍中找到的一些指南 并且我想确保在我的开发中遵循有关视图模型模式的良好实践 下面是一个博客实现的简单示例 您能否确认我走的路是正确的 假设我想在视图上显示帖子标题 描述 并