没有一堆隐藏字段的成功模型编辑

Cal*_*man 10 asp.net-mvc model-binding viewmodel valueinjecter

简而言之:如何成功编辑数据库条目而无需在编辑视图中包含模型的每个字段?

更新
所以我在DB(文章)中有一个项目.我想编辑一篇文章.我编辑的文章有很多属性(Id,CreatedBy,DateCreated,Title,Body).其中一些属性永远不需要更改(如Id,CreatedBy,DateCreated).因此,在我的编辑视图中,我只想要可以更改的字段的输入字段(如标题,正文).当我像这样实现编辑视图时,模型绑定失败.我没有提供输入的任何字段都设置为某个"默认"值(如DateCreated设置为01/01/0001 12:00:00 am).如果我确实为每个字段提供输入,一切正常,文章按预期编辑.我不知道是否正确地说"模型绑定失败"是必要的,如果"如果在编辑视图中没有为它们提供输入字段,则系统会填充包含不正确数据的字段".

如何以这样的方式创建编辑视图:我只需要为可以/需要编辑的字段提供输入字段,以便在调用Controller中的Edit方法时,正确填充DateCreated等字段,而不设置某些默认值,不正确的值?这是我目前的编辑方法:

    [HttpPost]
    public ActionResult Edit(Article article)
    {
        // Get a list of categories for dropdownlist
        ViewBag.Categories = GetDropDownList();


        if (article.CreatedBy == (string)CurrentSession.SamAccountName || (bool)CurrentSession.IsAdmin)
        {                
            if (ModelState.IsValid)
            {
                article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
                article.LastUpdated = DateTime.Now;
                article.Body = Sanitizer.GetSafeHtmlFragment(article.Body);

                _db.Entry(article).State = EntityState.Modified;
                _db.SaveChanges();
                return RedirectToAction("Index", "Home");
            }
            return View(article);
        }

        // User not allowed to edit
        return RedirectToAction("Index", "Home");   
    }
Run Code Online (Sandbox Code Playgroud)

编辑视图是否有帮助:

. . .
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>Article</legend>

    <p>
        <input type="submit" value="Save" /> | @Html.ActionLink("Back to List", "Index")
    </p>

    @Html.Action("Details", "Article", new { id = Model.Id })

    @Html.HiddenFor(model => model.CreatedBy)
    @Html.HiddenFor(model => model.DateCreated)

    <div class="editor-field">
        <span>
            @Html.LabelFor(model => model.Type)
            @Html.DropDownListFor(model => model.Type, (SelectList)ViewBag.Categories)
            @Html.ValidationMessageFor(model => model.Type)
        </span>
        <span>
            @Html.LabelFor(model => model.Active)
            @Html.CheckBoxFor(model => model.Active)
            @Html.ValidationMessageFor(model => model.Active)
        </span>
        <span>
            @Html.LabelFor(model => model.Stickied)
            @Html.CheckBoxFor(model => model.Stickied)
            @Html.ValidationMessageFor(model => model.Stickied)
        </span>            
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Title)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Title)
        @Html.ValidationMessageFor(model => model.Title)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.Body)
    </div>
    <div class="editor-field">
        @* We set the id of the TextArea to 'CKeditor' for the CKeditor script to change the TextArea into a WYSIWYG editor. *@
        @Html.TextAreaFor(model => model.Body, new { id = "CKeditor", @class = "text-editor" })
        @Html.ValidationMessageFor(model => model.Body)
    </div>
</fieldset>
. . .
Run Code Online (Sandbox Code Playgroud)

如果我遗漏这两个输入:

@Html.HiddenFor(model => model.CreatedBy)
@Html.HiddenFor(model => model.DateCreated)
Run Code Online (Sandbox Code Playgroud)

调用Edit方法时,它们将设置为默认值.CreatedBy设置为Null,Created设置为01/01/0001 12:00:00 am

为什么它们没有设置为数据库中当前设置的值?

Cal*_*man 10

经过一些研究后,我发现了一些辅助ViewModel过程的工具 - 一个是AutoMapper和其他InjectValues.我选择InjectValues主要是因为它不仅可以"展平"对象(地图对象a - > b),而且它还可以"展开"它们(地图对象b - > a) - 不幸的是,AutoMapper缺乏这样的东西.框 - 我需要做的事情才能更新数据库中的值.

现在,我创建了一个仅包含以下属性的ArticleViewModel,而不是将我的文章模型及其所有属性发送到我的视图中:

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

    [MaxLength(15)]
    public string Type { get; set; }

    public bool Active { get; set; }
    public bool Stickied { get; set; }

    [Required]
    [MaxLength(200)]
    public string Title { get; set; }

    [Required]
    [AllowHtml]
    public string Body { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

当我创建文章时,不是发送文章对象(包含每个属性),而是发送查看'更简单'的模型 - 我的ArticleViewModel:

//
// GET: /Article/Create

public ActionResult Create()
{
    return View(new ArticleViewModel());
}
Run Code Online (Sandbox Code Playgroud)

对于POST方法,我们将ViewModel发送到View并使用其数据在DB中创建新文章.我们通过将ViewModel"unflattening"到Article对象来实现:

//
// POST: /Article/Create
public ActionResult Create(ArticleViewModel articleViewModel)
{
    Article article = new Article();              // Create new Article object
    article.InjectFrom(articleViewModel);         // unflatten data from ViewModel into article 

    // Fill in the missing pieces
    article.CreatedBy = CurrentSession.SamAccountName;   // Get current logged-in user
    article.DateCreated = DateTime.Now;

    if (ModelState.IsValid)
    {            
        _db.Articles.Add(article);
        _db.SaveChanges();
        return RedirectToAction("Index", "Home");
    }

    ViewBag.Categories = GetDropDownList();
    return View(articleViewModel);            
}
Run Code Online (Sandbox Code Playgroud)

填写的"缺失的部分"是我不想在视图中设置的文章属性,也不需要在编辑视图中更新(或者根本不需要更新).

Edit方法几乎相同,除了不向View发送新的ViewModel,我们发送一个预先填充了来自数据库的数据的ViewModel.我们通过从DB检索文章并将数据展平到ViewModel来完成此操作.首先,GET方法:

    //
    // GET: /Article/Edit/5
    public ActionResult Edit(int id)
    {
        var article = _db.Articles.Single(r => r.Id == id);     // Retrieve the Article to edit
        ArticleViewModel viewModel = new ArticleViewModel();    // Create new ArticleViewModel to send to the view
        viewModel.InjectFrom(article);                          // Inject ArticleViewModel with data from DB for the Article to be edited.

        return View(viewModel);
    }
Run Code Online (Sandbox Code Playgroud)

对于POST方法,我们希望获取从View发送的数据并使用它更新存储在DB中的文章.要做到这一点,我们只需将ViewModel"unflattening"到Article对象上即可反转展平过程 - 就像我们对Create方法的POST版本所做的那样:

    //
    // POST: /Article/Edit/5
    [HttpPost]
    public ActionResult Edit(ArticleViewModel viewModel)
    {
        var article = _db.Articles.Single(r => r.Id == viewModel.Id);   // Grab the Article from the DB to update

        article.InjectFrom(viewModel);      // Inject updated values from the viewModel into the Article stored in the DB

        // Fill in missing pieces
        article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
        article.LastUpdated = DateTime.Now;

        if (ModelState.IsValid)
        {
            _db.Entry(article).State = EntityState.Modified;
            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }

        return View(viewModel);    // Something went wrong
    }
Run Code Online (Sandbox Code Playgroud)

我们还需要更改强类型的"创建和编辑"视图以期望ArticleViewModel而不是文章:

@model ProjectName.ViewModels.ArticleViewModel
Run Code Online (Sandbox Code Playgroud)

就是这样!

总而言之,您可以实现ViewModels,只将模型的各个部分传递给您的视图.然后,您可以只更新这些部分,将ViewModel传递回Controller,并使用ViewModel中的更新信息来更新实际模型.