在ASP.NET MVC中为读取,创建和更新操作分离ViewModel

时间:2016-08-27 07:29:21

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

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

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

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

创建: 创建新记录时,我不需要使用ID, EmailConfirmed AccessFailedCount 等。专栏。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

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

  1. 创建视图会将详细信息传递到包含用于创建新对象的字段的表单中。

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

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

  4. 因此虽然这两种形式的1和2都有共同的字段,但是存在管理Id传递给该表单的位置以及在程序流程中如何以及何时确定它的问题。

    虽然在2和3之间存在共享数据,但是视图的呈现完全不同,表格是一个表格,并且任何表单字段都需要只读取一个编辑表单以模仿详细信息视图的显示

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

    因此,无需将一些数据传递给视图以改变这些视图的显示方式,因此每个视图都有单独的显示,这是值得的。我认为这会提供一种不那么复杂的方法,并且可以减少使用ViewBag变量或javascript时可能产生的编码错误和安全问题,例如,使可编辑字段只读,改变一个视图页面的显示。 / p>

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

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

答案 1 :(得分:2)

我的答案很晚,但我希望它仍能帮助那些人。

我同意Yvette Colomb指出的内容,但我认为在某些情况下,最好只有1个视图和视图模型用于创建和更新操作。事实上,我甚至写了一篇关于它的博客文章,如果你有兴趣,可以在这里找到它: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充分发挥其潜力的详细信息,请参阅我的博客帖子。