ASP.NET MVC 中用于读取、创建和更新操作的单独 ViewModel

Separate ViewModel for Read, Create, and Update actions in ASP.NET MVC

我在 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中组合多个视图,这也是类似的情况。

Create: 新建记录的时候不需要用Id, EmailConfirmedAccessFailedCount 等列。

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

在这种情况下,ViewModel的最佳使用方法是什么?创建单独的 ViewModelReadUserViewModelCreateUserViewModelUpdateUserViewModel 或使用相同的ViewModel为同一组数据?任何帮助,将不胜感激。

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

重用项目中重复的 code/views 的最简单方法是使用局部视图。因此,如果页面的某个方面是重复的,一定要使用局部视图并将其合并到多个不同的视图中。

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

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

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

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

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

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

虽然2和3之间有共享数据,但是视图的呈现完全不同,一个表单说一个table,任何表单字段都需要设为只读,以便编辑表单模拟详细信息视图的显示。

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

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

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

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

我的回答已经很晚了,但我希望它仍然能帮助到那里的人。

我同意 Yvette Colomb 指出的内容,但我确实认为在某些情况下最好只有 1 个视图和 ViewModel 用于创建和更新操作。事实上,我什至写了一篇关于它的博客post,如果你有兴趣可以在这里找到它: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>
}

查看我的博客post,了解有关如何充分利用此 ViewModel 的更多信息。