2012-06-07 18 views
9

लघु में: मैं संपादित दृश्य के अंदर मॉडल के लिए प्रत्येक फ़ील्ड को शामिल किए बिना डीबी प्रविष्टि को सफलतापूर्वक कैसे संपादित करूं?छिपे हुए क्षेत्रों के समूह के बिना सफल मॉडल संपादन

अद्यतन
तो मैं डीबी (एक अनुच्छेद) में एक आइटम है। मैं एक लेख संपादित करना चाहता हूँ। मेरे द्वारा संपादित लेख में कई गुण हैं (आईडी, बनाया गया, डेटक्रेटेड, शीर्षक, बॉडी)। इनमें से कुछ गुणों को कभी भी बदलने की आवश्यकता नहीं है (जैसे आईडी, बनाया गया, डेटक्रेटेड)। तो मेरे संपादन दृश्य में, मैं केवल उन फ़ील्ड के लिए इनपुट फ़ील्ड चाहता हूं जिन्हें बदला जा सकता है (जैसे शीर्षक, बॉडी)। जब मैं इस तरह एक संपादन दृश्य लागू करता हूं, मॉडल बाध्यकारी विफल रहता है। कोई भी फ़ील्ड जिसे मैंने इनपुट की आपूर्ति नहीं की है, कुछ 'डिफ़ॉल्ट' मान पर सेट हो जाती है (जैसे डेटक्रेटेड 01/01/0001 12:00:00 बजे सेट हो जाता है)। यदि मैं प्रत्येक फ़ील्ड के लिए आपूर्ति इनपुट करता हूं, तो सब कुछ ठीक काम करता है और लेख अपेक्षित के रूप में संपादित किया जाता है। मुझे नहीं पता कि यह कहने में सही है कि "मॉडलिंग बाइंडिंग असफल हो जाती है", उतना ही "सिस्टम गलत डेटा वाले फ़ील्ड भरता है अगर संपादन दृश्य में उनके लिए कोई इनपुट फ़ील्ड नहीं दिया गया हो।"

मैं इस तरह से एक संपादन दृश्य कैसे बना सकता हूं कि मुझे केवल उन फ़ील्ड के लिए इनपुट फ़ील्ड की आपूर्ति करने की आवश्यकता है जिन्हें संपादन/आवश्यकता हो सकती है, ताकि जब नियंत्रक में संपादन विधि को कॉल किया जाता है, तो डेटक्रेटेड जैसे फ़ील्ड सही ढंग से आते हैं , और कुछ डिफ़ॉल्ट, गलत मान पर सेट नहीं है? यहाँ मेरी संपादित तरीका है के रूप में यह वर्तमान में खड़ा है: संपादित दृश्य

[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"); 
    } 

और अगर यह मदद करता है:

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

जब:

. . . 
@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> 
. . . 

मैं इन दो आदानों बाहर छोड़ने के लिए थे, तो संपादन विधि कहा जाता है, वे डिफ़ॉल्ट मान पर सेट हैं। CreatedBy, अशक्त पर सेट है निर्मित 01/01/0001 00:00:00

क्यों वे मान पर सेट नहीं हैं के रूप में वे वर्तमान में डीबी में सेट कर रहे हैं के लिए निर्धारित है?

+0

आप प्रत्येक फ़ील्ड को सेट करने के बजाय TryUpdateModel (मौजूदा आर्टिकल) का भी उपयोग कर सकते हैं। कुछ समाधान बहुत सुरुचिपूर्ण प्रतीत नहीं हो सकते हैं, लेकिन ऑटोमैपर जैसे समाधान हैं, जिनका आप बाद में उपयोग कर सकते हैं, जो इस समस्या को हल करेंगे, लेकिन अभी आपको इसे अभी तक नहीं जानना है। – LukLed

+0

लेकिन अगर यह "सही" तरीका है, तो निश्चित रूप से मैं इसे अभी जानना चाहता हूं;) मैं खुदाई कर रहा हूं और ऐसा लगता है, हां, "व्यूमोडल्स" नामक इन चीजों का उपयोग करने का तरीका है: ए "एक इकाई से जानकारी का एक टुकड़ा दिखाने के लिए रास्ता।" जाहिर है, यह थोड़ा और काम है, लेकिन मॉडल/विचार अधिक जटिल हो जाते हैं क्योंकि वे मजबूत होते हैं, जबकि इन अन्य "ब्रूट फोर्स" विधियों को तोड़ने लगते हैं। –

उत्तर

9

कुछ और शोधों के बाद मैं कुछ टूल पर आया जो व्यूमोडेल प्रक्रिया में सहायता करते हैं - एक ऑटोमैपर & अन्य इंजेक्शन वैल्यूज है। मैं मुख्य रूप से इंजेक्शन वैल्यू के साथ गया क्योंकि यह केवल ऑब्जेक्ट्स (नक्शा ऑब्जेक्ट ए -> बी) नहीं कर सकता है लेकिन यह उन्हें "unflatten" भी कर सकता है (नक्शा ऑब्जेक्ट बी -> ए) - कुछ ऐसा है जो ऑटोमैपर दुर्भाग्य से बाहर की बॉक्स की कमी करता है - मुझे डीबी के अंदर मूल्यों को अपडेट करने के लिए कुछ करने की आवश्यकता है।

अब, के बजाय अपने विचार करने के लिए अपने गुणों के सभी के साथ मेरी अनुच्छेद मॉडल भेजने की, मैं एक 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 ले हम देखें करने के लिए भेजा और इसके डेटा का उपयोग DB में एक नया अनुच्छेद बनाने के लिए । हम एक अनुच्छेद ऑबजेक्ट पर "unflattening" ViewModel ऐसा करते हैं:

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

में भरा "टुकड़े लापता" अनुच्छेद गुण मैं दृश्य में स्थापित करने के लिए नहीं करना चाहता था कर रहे हैं, न ही वे में अद्यतन करने की आवश्यकता कर संपादन दृश्य (या बिल्कुल, उस मामले के लिए)।

संपादन विधि एक बहुत ही व्यू मॉडेल भेजने के बजाय, दृश्य में बहुत अधिक है, हम अपने डीबी से डेटा के साथ पूर्व-पॉप्युलेट भेजते हैं। हम डीबी से आलेख को पुनर्प्राप्त करके और डेटा को 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 पद्धति के लिए हम देखें से भेजे गए डेटा ले और इसके साथ डीबी में संग्रहीत अनुच्छेद अपडेट करना चाहते हैं। ऐसा करने के लिए हम केवल अनुच्छेद ऑबजेक्ट पर 'unflattening' ViewModel द्वारा सपाट प्रक्रिया को उल्टा - जैसे हम अपने बनाएं विधि के पद संस्करण के लिए किया था:

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

हम भी बदलने के लिए दृढ़ता से टाइप बनाएं जरूरत & संपादित विचारों एक ArticleViewModel उम्मीद करने के लिए एक लेख के बजाय:

@model ProjectName.ViewModels.ArticleViewModel 

और बस हो गया!

तो संक्षेप में, आप अपने मॉडलों के टुकड़े को अपने दृश्यों में पास करने के लिए व्यूमोडल्स को कार्यान्वित कर सकते हैं। फिर आप केवल उन टुकड़ों को अपडेट कर सकते हैं, व्यूमोडेल को कंट्रोलर पर वापस भेज सकते हैं, और वास्तविक मॉडल अपडेट करने के लिए व्यूमोडेल में अपडेट की गई जानकारी का उपयोग कर सकते हैं।

+0

इस प्रश्न को अद्यतित रखने के लिए धन्यवाद। ध्यान देने योग्य एक अंतिम बात यह सुनिश्चित करें कि आपका आईडी आपके दृश्य में एक छिपी हुई फ़ील्ड है: '@ एचटीएमएल। हाइडफ़ोर (मॉडल => मॉडल.आईडी)' – Rocky

+0

+1 [कोडप्लेक्स] से वैल्यू इंजेक्टर खोजने के लिए +1 (https: // valueinjecter .codeplex.com /)। यह एक प्यारी छोटी पुस्तकालय है। – Daz

2

देखें मॉडल उदाहरण:

public class ArticleViewModel { 
    [Required] 
    public string Title { get; set; } 

    public string Content { get; set; } 
} 

बाइंडिंग उदाहरण

public ActionResult Edit(int id, ArticleViewModel article) { 
    var existingArticle = db.Articles.Where(a => a.Id == id).First(); 
    existingArticle.Title = article.Title; 
    existingArticle.Content = article.Content; 
    db.SaveChanges(); 
} 

सरल उदाहरण है कि है, लेकिन आप ModelState पर गौर करना चाहिए की जांच करने के लिए यदि मॉडल, त्रुटि नहीं है प्राधिकरण की जाँच करें और इस के लिए कदम सेवा कक्षाओं में नियंत्रक से बाहर कोड, लेकिन यह एक और सबक है।

यह सही है विधि संपादित करें:

[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) 
     { 
      var existingArticle = _db.Articles.First(a => a.Id = article.Id); 
      existingArticle.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName; 
      existingArticle.LastUpdated = DateTime.Now; 
      existingArticle.Body = Sanitizer.GetSafeHtmlFragment(article.Body); 
      existingArticle.Stickied = article.Stickied; 

      _db.SaveChanges(); 
      return RedirectToAction("Index", "Home"); 
     } 
     return View(article); 
    } 

    // User not allowed to edit 
    return RedirectToAction("Index", "Home"); 
} 
+0

तो व्यूमोडेल के साथ आपको मॉडल बाइंडर (कम से कम डिफ़ॉल्ट) को सफलतापूर्वक मॉडल से बांधने के लिए ** ** ** ** के लिए एक इनपुट प्रदान करने की आवश्यकता है, क्या यह सही है? आपके उदाहरण में मुझे "शीर्षक" और "सामग्री" दोनों फ़ील्ड के लिए इनपुट प्रदान करने की आवश्यकता होगी। उस स्थिति में मुझे बस इतना करना आसान लगेगा कि मैं जो कर रहा हूं - गैर-बदलते क्षेत्रों (आईडी, डेटक्रेटेड इत्यादि) के दृश्य में छुपे हुए फ़ील्ड प्रदान करें। यह मुझे मेरे प्रत्येक मॉडल के लिए एक दृश्य मॉडल बनाने की आवश्यकता से बचाएगा। –

+0

@cbergman: एमवीसी खेला जाने वाला तरीका है। छिपे हुए क्षेत्रों को आसानी से छेड़छाड़ और बदला जा सकता है। छुपा क्षेत्र बनाना मूल्यों को बदलने से नहीं बचाता है। कोई भी अभी भी छुपा हुआ क्षेत्र बदल सकता है और कुछ संशोधित कर सकता है, जिसे आप संशोधित नहीं करना चाहते थे। कृपया इस हालिया प्रश्न को पढ़ें: http://stackoverflow.com/questions/10928287/asp-net-mvc-database-entities-or-viewmodels/10928739#10928739 – LukLed

+0

मैं सोच रहा था कि मॉडल बाइंडर एक निश्चित खोज पाएगा इसके उत्तीर्ण आईडी द्वारा अनुच्छेद, प्रत्येक फ़ील्ड को स्वयं बनाते हैं (जिसमें पहले से ही मान होते हैं), और केवल उन सभी फ़ील्ड को संशोधित करें जिन्हें दृश्य के अंदर संपादित किया गया है। लेकिन जाहिर है (जैसा कि मेरे साथ मामला है, वैसे भी) यदि इसे व्यू द्वारा पारित उस क्षेत्र के लिए इनपुट नहीं मिलता है, तो वह उस फ़ील्ड को "डिफ़ॉल्ट" मान के साथ पॉप्युलेट करेगा (उदाहरण के लिए '01/01 के साथ दिनांक दिनांक फ़ील्ड/0001 12:00:00 बजे ')। यदि डेटटाइम फ़ील्ड कहता है, दिनांकित क्षेत्र, स्पष्ट रूप से यह गलत होगा। –

0

उपयोग ViewModels

इस मुद्दे के समाधान को खोजने के अपने निरंतर शोध के माध्यम से मेरा मानना ​​है कि "व्यूमोडल्स" नामक इन चीजों का उपयोग करने का तरीका है। जैसा कि जिमी बोगर्ड द्वारा post में बताया गया है, व्यूमोडल्स "एक इकाई से जानकारी का एक टुकड़ा दिखाने का एक तरीका है।"

asp.net-mvc-view-model-patterns मुझे सही रास्ते पर नेतृत्व में मिला,। मैं अभी भी बाह्य संसाधनों लेखक आगे ViewModel अवधारणा (जिमी उनमें से एक होने से ब्लॉग पोस्ट) समझ में पोस्ट की गई कुछ बाहर की जाँच कर रहा हूँ

2

एक और अच्छा तरीका viewmodel

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

    article.Stickied = article0.Stickied; 

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

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

    return View(article0); // Something went wrong 
} 
संबंधित मुद्दे