沒有一堆隱藏欄位的成功模型編輯
簡而言之:如何成功編輯數據庫條目,而無需在編輯視圖中包含模型的每個欄位?
更新
所以我在數據庫中有一個項目(一篇文章)。我想編輯一篇文章。我編輯的文章有很多屬性(Id、CreatedBy、DateCreated、Title、Body)。其中一些屬性永遠不需要更改(如 Id、CreatedBy、DateCreated)。所以在我的編輯視圖中,我只想要可以更改的欄位(如標題、正文)的輸入欄位。當我實現這樣的編輯視圖時,模型綁定失敗。我沒有提供輸入的任何欄位都設置為某個“預設”值(例如 DateCreated 設置為 01/01/0001 12:00:00am)。如果我這樣做為每個欄位提供輸入,一切正常,文章按預期編輯。我不知道說“模型綁定失敗”是否正確,就像“如果在編輯視圖中沒有為它們提供輸入欄位,系統會用不正確的數據填充欄位”。
如何以只需要為可以/需要編輯的欄位提供輸入欄位的方式創建編輯視圖,以便在呼叫 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"); }如果有幫助,請編輯視圖:
. . . @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> . . .如果我要省略這兩個輸入:
@Html.HiddenFor(model => model.CreatedBy) @Html.HiddenFor(model => model.DateCreated)當呼叫 Edit 方法時,它們被設置為預設值。CreatedBy 設置為Null, Created 設置為01/01/0001 12:00:00am
為什麼它們沒有設置為目前在數據庫中設置的值?
經過更多研究後,我發現了一些有助於 ViewModel 流程的工具——一個是 AutoMapper,另一個是 InjectValues。我選擇 InjectValues 主要是因為它不僅可以“扁平化”對象(映射對象 a -> b),而且還可以“不扁平化”它們(映射對象 b -> a)——不幸的是,AutoMapper 缺乏的東西- box - 為了更新數據庫中的值,我需要做的事情。
現在,我沒有將我的 Article 模型及其所有屬性發送到我的視圖,而是創建了一個僅包含以下屬性的 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; } }當我創建一篇文章時,我不是發送一個文章對象(帶有每個屬性),而是向視圖發送一個“更簡單”的模型 - 我的 ArticleViewModel:
// // GET: /Article/Create public ActionResult Create() { return View(new ArticleViewModel()); }對於 POST 方法,我們將 ViewModel 發送到 View 並使用其數據在數據庫中創建新文章。我們通過將 ViewModel “展開”到 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); }填寫的“缺失部分”是我不想在視圖中設置的文章屬性,也不需要在編輯視圖中更新它們(或者根本不需要)。
Edit 方法幾乎相同,除了發送一個新的 ViewModel 到 View 之外,我們發送一個預先填充了我們數據庫中的數據的 ViewModel。我們通過從數據庫中檢索文章並將數據展平到 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); }對於 POST 方法,我們希望獲取從視圖發送的數據並用它更新儲存在數據庫中的文章。為此,我們只需通過將 ViewModel ‘unflatten’ 到 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 }我們還需要更改強類型的 Create & Edit 視圖以期望 ArticleViewModel 而不是 Article:
@model ProjectName.ViewModels.ArticleViewModel就是這樣!
所以總而言之,您可以實現 ViewModel 以將模型的一部分傳遞給您的視圖。然後,您可以只更新這些部分,將 ViewModel 傳遞回 Controller,並使用 ViewModel 中的更新資訊來更新實際模型。