【问题标题】:Separate ViewModel for Read, Create, and Update actions in ASP.NET MVCASP.NET MVC 中用于读取、创建和更新操作的单独 ViewModel
【发布时间】:2017-01-03 20:29:09
【问题描述】:

我在ASP.NET MVC 项目中使用相同的ViewModel,但是对于一千条记录,最好不要从数据库中检索未使用的记录。例如,假设UserViewModel 用于ReadCreateUpdate 情况,如下所示:

public class UserViewModel
{
    public int Id { get; set; }

    public string Email { get; set; }

    public string Password { get; set; }

    public string ConfirmPassword { get; set; }

    public bool EmailConfirmed { get; set; }        

    public virtual int AccessFailedCount { get; set; }

    public virtual bool LockoutEnabled { get; set; }

    public virtual DateTime? LockoutEndDateUtc { get; set; }

    public virtual string PasswordHash { get; set; }

    public virtual string PhoneNumber { get; set; }

    public virtual bool PhoneNumberConfirmed { get; set; }

    public virtual string SecurityStamp { get; set; }

    public virtual bool TwoFactorEnabled { get; set; }
}

阅读: 在显示记录的详细信息时,我需要检索除密码之外的所有属性(我知道我也可以在没有ViewModel 的情况下检索数据,但有时我需要在一个 ViewModel 中组合多个视图,这也是类似的情况。

创建:创建新记录时,不需要使用Id、EmailConfirmedAccessFailedCount等列。

更新:更新记录时,我也不需要使用某些属性。

在这个场景下,使用ViewModel的最佳方法是什么?要创建单独的ViewModel,即ReadUserViewModelCreateUserViewModelUpdateUserViewModel,还是为同一组数据使用相同的ViewModel?任何帮助,将不胜感激。

【问题讨论】:

  • view model 的目的是表示视图中的数据,以便单个视图模型有意义。而且您始终可以拥有一个基本视图模型,其中包含所有人共有的属性以避免重复。
  • @StephenMuecke 我以前读过that useful post you suggested。在这种情况下,您的意思是不需要使用单独的视图模型,最好为每个操作使用相同的视图模型,即读取、创建、更新?如果是这样,我们是否应该在检索数据时也使用 ViewModel 而不是直接从 DbContext 中检索数据?在 Controller 中,我从数据库中检索它并填充到 ViewModel,然后再返回 View。这是一个好方法吗?
  • 就我个人而言,我总是使用特定于视图的视图模型(在您的情况下,有一个 class UserBaseVM 包含所有视图共有的属性(例如 Email),然后有一个视图模型对于从UserBaseVM 继承的每个视图,例如class UserCreateVM : UserBaseVM 将包含特定于创建新用户的属性(例如ConfirmPassword
  • 是的,似乎更好更干净。关于我问题的第二部分的任何想法(......我们是否应该在检索数据时也使用 ViewModel......)?
  • 抱歉,不确定您的意思。通常你会使用var model = db.Users.Select(x => new UserVM() { Email = x.Email, ..... });之类的东西来调用数据库并将数据投影到视图模型中,然后再将其返回到视图中(或使用automapper等工具

标签: asp.net asp.net-mvc asp.net-mvc-4 viewmodel asp.net-mvc-viewmodel


【解决方案1】:

我的回答很晚,但我希望它仍然可以帮助那里的人。

我同意 Yvette Colomb 指出的内容,但我确实认为在某些情况下最好只使用 1 个视图和 ViewModel 来进行创建和更新操作。其实我什至写过一篇博文,有兴趣的可以在这里找到:https://blog.sandervanlooveren.be/posts/mvc-best-practices-for-create-update/

基本上你要做的是创建一个 baseViewModel,它知道你是在创建还是更新,并在你的视图中使用它。在大多数情况下,用户可以编辑的属性与他在创建时可以填写的属性相同。

你的 baseViewModel 可能看起来像这样(我让它尽可能可重用):

public abstract class BaseFormViewModel<T>
{
   public bool IsUpdate => !GetId().Equals(default(T));

   protected abstract T GetId();

   /// <summary>
   /// Gets the action name based on the lambda supplied
   /// </summary>
   /// <typeparam name="TController"></typeparam>
   /// <param name="action"></param>
   /// <returns></returns>
   protected string GetActionName<TController>(Expression<Func<TController, ActionResult>> action) where TController : Controller
   {
       return ((MethodCallExpression)action.Body).Method.Name;
   }
}

在你看来,你可以有这样的东西:

@using (Html.BeginForm(Model.ActionName, "Person", FormMethod.Post, new  { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    if (Model.IsUpdate)
    {
        @Html.HiddenFor(m => m.Person.Id)
    }
    <div class="form-group">
        @Html.LabelFor(m => m.Person.Firstname, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Person.Firstname, new { @class = "form-control" })
            @Html.ValidationMessageFor(m => m.Person.Firstname)
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Person.Lastname, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Person.Lastname, new { @class = "form-control" })
            @Html.ValidationMessageFor(m => m.Person.Lastname)
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Save" />
        </div>
    </div>
}

有关如何充分利用此 ViewModel 的更多信息,请参阅我的博文。

【讨论】:

    【解决方案2】:

    我会使用单独的视图模型来获取详细信息(只读查看)、创建和编辑。

    重用项目中重复的代码/视图的最简单方法是使用局部视图。因此,如果页面的某个方面是重复的,请务必使用部分视图并将其合并到多个不同的视图中。

    关于创建/更新或查看详细信息,IMO 为这些单独的视图使用单独的视图类型更简单。想要模块化代码而不是重复代码是可以理解的,但有一点是,试图实现最大模块化的复杂性可能超过在表单中​​重复某些字段。

    “创建”视图会显示一个空表单,因此您不会从数据库中获取任何值或重新使用数据本身,而是将数据添加到数据库中。因此,为此重用相同视图的收益有限。

    1. 创建视图会将详细信息传递到带有字段的表单中以创建新对象。

    2. 编辑视图将传递该对象的 Id 以填充表单字段,并将其与 Id 一起传递给控制器​​。

    3. 详细信息视图将传递该对象的 Id 以填充表的字段(可能)并且不需要表单集合。

    因此,尽管 1 和 2 这两个表单具有共同的字段,但存在管理将 Id 传递给该表单的位置以及如何以及何时在程序流程中确定这一点的问题。

    虽然 2 和 3 之间存在共享数据,但视图的呈现方式完全不同,表单表示表格,任何表单字段都需要设为只读,编辑表单才能模拟详细信息视图的显示.

    重用视图来显示只读详细信息和编辑对象之间的问题是,您需要实现一些更复杂的方法来禁用字段为只读。除非您不介意显示可编辑字段的显示(不是最好的 UX imo)。

    因此,无需向视图传递一些数据来改变这些视图的显示方式,为每个视图分别显示是值得的。我认为这将提供一种不太复杂的方法,并有助于减少使用 ViewBag 变量或 javascript 可能产生的编码错误和安全问题,例如,使可编辑字段只读,改变一个视图页面的呈现。

    另一个注意事项:对于密码重置(可能是用户编辑),我总是单独实现这一点,这增加了您共享视图和数据表示的想法的分量,但是数据表示的差异足以保证不同的视图.

    话虽如此,我已经开发了一个应用程序,它对某些用户是只读的,具体取决于身份验证角色。如前所述,这需要在整个项目的服务器端和客户端进行仔细处理,以确保未经授权的用户无法编辑或删除任何只读视图(在这种情况下,这比复制视图更容易)。在一般用途中,我建议将单独的视图用于单独的功能。这样,代码就可以根据特定目的与该视图(或部分视图)进行交互。

    【讨论】:

    • 非常感谢您的回答。正如斯蒂芬所说,从包含相同属性的模型继承也应该是个好主意。我会记住你所有的解释。投票+
    • 另一方面,如果你有过 ASP.NET Identity 的经验,你能看看this 严重的问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多