Asp.net-Mvc

沒有一堆隱藏欄位的成功模型編輯

  • August 28, 2015

簡而言之:如何成功編輯數據庫條目,而無需在編輯視圖中包含模型的每個欄位?

更新

所以我在數據庫中有一個項目(一篇文章)。我想編輯一篇文章。我編輯的文章有很多屬性(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 中的更新資訊來更新實際模型。

引用自:https://stackoverflow.com/questions/10935803