2009-04-05 17 views
12

के साथ मॉडलस्टेट अपडेट करना समस्या: पोस्टिंग + सत्यापन स्थिति में मॉडलस्टेट को अपडेट करने के लिए कैसे करें।मॉडल ऑब्जेक्ट

मैं एक सरल रूप मिल गया है:

<%= Html.ValidationSummary() %> 
<% using(Html.BeginForm())%> 
<%{ %> 
    <%=Html.TextBox("m.Value") %> 
    <input type="submit" /> 
<%} %> 

उपयोगकर्ता द्वारा सबमिट जब मैं इनपुट को मान्य करना चाहते हैं और कुछ परिस्थितियों में मैं उपयोगकर्ता की त्रुटियों को ठीक, दे उसे पता है कि वह गलती से हुआ है कि चाहते हैं पहले से ही तय हो:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(M m) 
{ 
    if (m.Value != "a") 
    { 
     ModelState.AddModelError("m.Value", "should be \"a\""); 
     m.Value = "a"; 
     return View(m); 
    } 
    return View("About");    
} 

खैर समस्या है, MVC बस मॉडल को देखने के लिए पारित कर दिया पर ध्यान नहीं देगा और फिर से प्रस्तुत करना होगा जो कुछ भी उपयोगकर्ता द्वारा लिखा गया है - और नहीं मेरी मूल्य ("एक")। ऐसा होता है, क्योंकि टेक्स्टबॉक्स रेंडरर जांचता है कि मॉडलस्टेट है और यदि यह शून्य नहीं है - मॉडलस्टेट का मान उपयोग किया जाता है। यह मान निश्चित रूप से पोस्ट करने से पहले एक उपयोगकर्ता टाइप किया गया है।

चूंकि मैं टेक्स्टबॉक्स रेंडरर के व्यवहार को नहीं बदल सकता क्योंकि मुझे लगता है कि केवल एक ही समाधान मॉडलस्टेट को अपडेट करना होगा। Quick'n'dirty तरीका है (ab) DefaultModelBinder का उपयोग करें और उस विधि को ओवरराइड करें जो असाइनमेंट दिशा को बदलकर फ़ॉर्म से मानों को मॉडल में निर्दिष्ट करता है;)। DefaultModelBinder का उपयोग करके मुझे आईडी को पार्स करने की आवश्यकता नहीं है।

/// <summary> 
    /// Updates ModelState using values from <paramref name="order"/> 
    /// </summary> 
    /// <param name="order">Source</param> 
    /// <param name="prefix">Prefix used by Binder. Argument name in Action (if not explicitly specified).</param> 
    protected void UpdateModelState(object model, string prefix) 
    { 
     new ReversedBinder().BindModel(this.ControllerContext, 
      new ModelBindingContext() 
      { 
       Model = model, 
       ModelName = prefix, 
       ModelState = ModelState, 
       ModelType = model.GetType(), 
       ValueProvider = ValueProvider 
      }); 
    } 

    private class ReversedBinder : DefaultModelBinder 
    { 
     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
     { 
      string prefix = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name); 
      object val = typeof(Controller) 
       .Assembly.GetType("System.Web.Mvc.DictionaryHelpers") 
       .GetMethod("DoesAnyKeyHavePrefix") 
       .MakeGenericMethod(typeof(ValueProviderResult)) 
       .Invoke(null, new object[] { bindingContext.ValueProvider, prefix }); 
      bool res = (bool)val; 
      if (res) 
      { 

       IModelBinder binder = new ReversedBinder();//this.Binders.GetBinder(propertyDescriptor.PropertyType); 
       object obj2 = propertyDescriptor.GetValue(bindingContext.Model); 

       ModelBindingContext context2 = new ModelBindingContext(); 
       context2.Model = obj2; 
       context2.ModelName = prefix; 
       context2.ModelState = bindingContext.ModelState; 
       context2.ModelType = propertyDescriptor.PropertyType; 
       context2.ValueProvider = bindingContext.ValueProvider; 
       ModelBindingContext context = context2; 
       object obj3 = binder.BindModel(controllerContext, context); 

       if (bindingContext.ModelState.Keys.Contains<string>(prefix)) 
       { 
        var prefixKey = bindingContext.ModelState.Keys.First<string>(x => x == prefix); 
        bindingContext.ModelState[prefixKey].Value 
            = new ValueProviderResult(obj2, obj2.ToString(), 
                   bindingContext.ModelState[prefixKey].Value.Culture); 
       } 
      } 
     } 
    } 

तो सवाल रहता है:: मैं कुछ अत्यंत असामान्य कर रहा हूँ या मैं कुछ याद आ रही है निम्नलिखित कोड (DefaultModelBinder के मूल कार्यान्वयन के आधार पर) यह करने के लिए अपने समाधान है? यदि पूर्व, तो मैं इस तरह की कार्यक्षमता को बेहतर तरीके से कैसे कार्यान्वित कर सकता हूं (मौजूदा एमवीसी आधारभूत संरचना का उपयोग करके)?

उत्तर

4

आप अपने नियंत्रक में अपने मॉडल ऑब्जेक्ट की बजाय पैरामीटर के रूप में एक फॉर्म संग्रह स्वीकार कर सकते हैं, जैसे: public ActionResult Index(FormCollection Form)

डिफ़ॉल्ट मॉडल बाइंडर मॉडल स्थिति को अपडेट नहीं करेगा, और आपको वह व्यवहार मिलेगा जो आप चाहते हैं।

संपादित करें: या आप मॉडल में अपने परिवर्तनों को दर्शाने के लिए मॉडलस्टेट डिक्शनरी को अपडेट कर सकते हैं।


[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(M m) 
{ 
    if (m.Value != "a") 
    { 
     ModelState["m.Value"].Value = new ValueProviderResult("a", m.Name, 
        CultureInfo.CurrentCulture); 
     ModelState.AddModelError("m.Value", "should be \"a\""); 
     m.Value = "a"; 
     return View(m); 
    } 
    return View("About");    
} 

नोट: मुझे यकीन नहीं है कि यह सबसे अच्छा तरीका है या नहीं। लेकिन ऐसा लगता है कि यह काम करता है और यह वह व्यवहार होना चाहिए जो आप चाहते हैं।

+0

लेकिन मैं डिफ़ॉल्ट बाध्यकारी प्राप्त करना चाहता हूं। मुझे यह चाहिए क्योंकि मैं मॉडलस्टेट का उपयोग करना चाहता हूं। मैं अपने मॉडल ऑब्जेक्ट में बदलावों को दर्शाने के लिए मॉडलस्टेट को अपडेट करना चाहता हूं। – user87338

+0

कृपया मेरा संपादन देखें। –

+0

आपकी टिप्पणी बिल्कुल वैसा ही है जो मैं कर रहा हूं, लेकिन आप इसे स्वयं करते हैं और मैं ते डेल्ट बाइंडर का उपयोग कर रहा हूं, इसलिए मेरे पास कुछ और "जेनेरिक" है। मूल्य ("ए") का परिवर्तन निम्न स्तर में होता है, इसलिए मुझे वास्तव में पता नहीं है कि प्रोप क्या बदल गए हैं। और आप मॉडलस्टेट ["एम। वैल्यू"] नहीं चाहते हैं। वैल्यू = नया वैल्यूप्रोवाइडर रीसेट ("ए", एम। एनएएम, संस्कृतिइन्फो। कंटेंटकल्चर); प्रत्येक 12 ऑब्जेक्ट की संपत्ति के लिए , क्या आप :)। – user87338

22

मुझे पता है कि यह पोस्ट काफी पुराना है, लेकिन यह एक समस्या है जो मैंने पहले किया था और मैंने बस एक साधारण समाधान के बारे में सोचा था जो मुझे पसंद है - पोस्ट मूल्य प्राप्त करने के बाद मॉडलस्टेट को साफ़ करें।

UpdateModel(viewModel); 
ModelState.Clear(); 

viewModel.SomeProperty = "a new value"; 
return View(viewModel); 

और दृश्य को मॉडलस्टेट की बजाय मॉडल (संभवतः संशोधित) मॉडल ऑब्जेक्ट का उपयोग करना होगा।

शायद यह वास्तव में स्पष्ट है। ऐसा लगता है कि हिंडसाइट में!

0

क्या मैं कुछ असामान्य कर रहा हूं या क्या मुझे कुछ याद आ रही है?

मुझे लगता है कि यह बहुत दुर्लभ है। मुझे लगता है कि एमवीसी मानता है कि सत्यापन त्रुटियां हां/कोई संबंध नहीं हैं, और इस मामले में आप सामान्य उपयोगकर्ता प्रतिक्रिया देने के साधन के रूप में सत्यापन त्रुटि का उपयोग कर रहे हैं।

मुझे लगता है कि एमवीसी भी सबसे सुखद लगता है जब POST या तो सत्यापन त्रुटियों के कारण विफल हो जाते हैं, या कोई क्रिया करते हैं और रीडायरेक्ट करते हैं या कुछ अलग-अलग प्रस्तुत करते हैं। मॉडल सत्यापन त्रुटियों के बाहर, एक ही इनपुट को फिर से प्रस्तुत करना बहुत दुर्लभ है।

मैं लगभग एक साल के लिए एमवीसी का उपयोग कर रहा हूं और बस एक अन्य संदर्भ में इसमें भाग गया, जहां एक पोस्ट के बाद मैं प्रतिक्रिया के रूप में एक नया रूप प्रस्तुत करना चाहता था।

[HttpPost] 
public ActionResult Upload(DocumentView data) { 
    if(!ModelState.IsValid) return View(data); 
    ProcessUpload(data); 
    return View(new DocumentView()); 
} 

MVC data से ModelState प्रतिपादन किया जाता है, न मेरी नई वस्तु। बेहद आश्चर्यजनक।

तो पूर्व, तो मैं कैसे ऐसी कार्यक्षमता एक बेहतर तरीका

  1. जावास्क्रिप्ट में स्वत: फिक्स करने के लिए लागू करने में लागू (संभव नहीं हो सकता) कर सकता है
  2. स्वत: सुधारों की सूची रखने के बनाया गया है, यदि ऑब्जेक्ट उन सभी के बाद मान्य है, तो इसे "इसके बारे में" दृश्य में पास करें और "एम सहेजे गए, निम्न सुधारों के साथ ..." जैसे संदेश के रूप में प्रदर्शित करें।
संबंधित मुद्दे