2009-04-30 27 views
9

मैं सभी नमूनों और ट्यूटोरियल और इस तरह के साथ ब्लॉक के चारों ओर गया है के बाद एक नई परियोजना के लिए MVC उपयोग करने के लिए कोशिश कर रहा हूँ। हालांकि, मुझे मुश्किल समय लग रहा है कि कुछ चीजें कहाँ होनी चाहिए।ASP.NET MVC मॉडल बंधन और सत्यापन सवाल

उदाहरण के तौर पर, मेरे पास प्रोफ़ाइल नामक एक इकाई है। इस इकाई में डेटटाइफ प्रॉपर्टी के साथ सामान्य प्रोफ़ाइल प्रकार की चीज़ें होती हैं जो डेटटाइम प्रकार की होती हैं। एचटीएमएल फॉर्म पर, जन्म फ़ील्ड की तारीख को 3 फ़ील्ड में विभाजित किया जाता है। अब, मुझे पता है कि मैं इसे संभालने के लिए एक कस्टम मॉडल बाइंडर का उपयोग कर सकता हूं, लेकिन अगर दर्ज की गई तारीख वैध तिथि नहीं है तो क्या होगा? क्या मुझे मॉडल बाइंडर में इसकी जांच करनी चाहिए? क्या मेरे सभी सत्यापन मॉडल बाइंडर में जाना चाहिए? क्या मॉडल बाइंडर में केवल कुछ चीजें मान्य हैं और शेष को नियंत्रक या मॉडल में ही मान्य करना ठीक है?

यहाँ कोड मैं अब है, लेकिन यह सिर्फ मेरे लिए सही नहीं लगता है। गंदे या बदबूदार लगता है।

namespace WebSite.Models 
{ 
    public class ProfileModelBinder : IModelBinder 
    { 
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      DateTime birthDate; 

      var form = controllerContext.HttpContext.Request.Form; 
      var state = controllerContext.Controller.ViewData.ModelState; 

      var profile = new Profile(); 
      profile.FirstName = form["FirstName"]; 
      profile.LastName = form["LastName"]; 
      profile.Address = form["Address"]; 
      profile.Address2 = form["Address2"]; 
      profile.City = form["City"]; 
      profile.State = form["State"]; 
      profile.Zip = form["Zip"]; 
      profile.Phone = form["Phone"]; 
      profile.Email = form["Email"]; 
      profile.Created = DateTime.UtcNow; 
      profile.IpAddress = controllerContext.HttpContext.Request.UserHostAddress; 

      var dateTemp = string.Format("{0}/{1}/{2}", 
       form["BirthMonth"], form["BirthDay"], form["BirthYear"]); 

      if (string.IsNullOrEmpty(dateTemp)) 
       state.AddModelError("BirthDate", "Required"); 
      else if (!DateTime.TryParse(dateTemp, out birthDate)) 
       state.AddModelError("BirthDate", "Invalid"); 
      else 
       profile.BirthDate = birthDate; 

      return profile; 
     }   
    } 
} 

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

+0

क्या आप मॉडल सत्यापन या फॉर्म सत्यापन की तलाश में हैं? मैं दोनों की सिफारिश करेंगे। इस तरह आप मॉडल को moq कर सकते हैं और साथ ही फ्रंटएंड पर एक समृद्ध यूआई प्रदान कर सकते हैं। –

उत्तर

5

मुझे लगता है कि मॉडल बाइंडर में सत्यापन करना उचित है।क्रेग बताते हैं, मान्यता ज्यादातर अपना व्यवसाय डोमेन की संपत्ति है, तथापि:

  1. कभी कभी अपने मॉडल सिर्फ एक गूंगा प्रस्तुति मॉडल, नहीं एक व्यापार वस्तु है
  2. वहाँ विभिन्न तंत्र आप सतह के लिए उपयोग कर सकते हैं मॉडल बांधने की मशीन में सत्यापन ज्ञान।

थॉमस आपको # 1 का एक्समैपल देता है।

# 2 का एक उदाहरण यह है कि जब आप विशेष रूप से गुणों का उपयोग करते हुए प्रमाणीकरण ज्ञान (जैसे डेटा एनीोटेशन विशेषता [आवश्यक]) का उपयोग करते हैं, या कस्टम मॉडल बाइंडर में कुछ व्यावसायिक परत सत्यापन सेवा इंजेक्ट करते हैं। इन परिस्थितियों में मॉडल बाइंडर सत्यापन की देखभाल करने के लिए एक आदर्श स्थान है।

कहा जा रहा है कि मॉडल बाध्यकारी (खोजना, परिवर्तित करना, और किसी ऑब्जेक्ट में डेटा को शफल करना) और सत्यापन (डेटा हमारे विनिर्देशों को पूरा करता है) दो अलग-अलग चिंताओं हैं। आप तर्क दे सकते हैं कि वे अलग-अलग चरणों/घटकों/एक्स्टेंसिबिलिटी पॉइंट्स होना चाहिए, लेकिन हमारे पास हमारे पास है, हालांकि DefaultModelBinder इन दो जिम्मेदारियों के बीच कुछ अंतर बनाता है। यदि आप जो कुछ करना चाहते हैं, वह किसी विशिष्ट प्रकार की ऑब्जेक्ट के लिए कुछ सत्यापन प्रदान करता है जिसे आप डिफॉल्टमोडेल बाइंडर से प्राप्त कर सकते हैं और संपत्ति स्तर सत्यापन के लिए OnPropertyValidating विधि को ओवरराइड कर सकते हैं या यदि आपको समग्र दृश्य की आवश्यकता है तो OnModelUpdated।

यहां मेरे पास कोड है, लेकिन यह बस मुझे सही नहीं लग रहा है। गंदा या बदबूदार लगता है।

आपके विशिष्ट कोड के लिए मैं केवल डेटटाइम के लिए मॉडल बाइंडर लिखने का प्रयास करूंगा। डिफॉल्ट मॉडल बाइंडर बाध्यकारी प्रथम नाम, अंतिम नाम इत्यादि का ख्याल रख सकता है, और प्रोफ़ाइल पर डेटटाइम संपत्ति तक पहुंचने पर आपके कस्टम मॉडल बाइंडर को प्रतिनिधि बना सकता है। इसके अलावा, सीधे फॉर्म पर जाने के बजाय बाध्यकारी कॉन्टेक्स्ट में ValueProvider का उपयोग करने का प्रयास करें। ये चीजें आपको अधिक लचीलापन दे सकती हैं।

यहां अधिक विचार: 6 Tips for ASP.NET MVC Model Binding

+0

यह सर्वर-साइड सत्यापन के लिए काम करना चाहिए (जब फॉर्म पोस्ट किया गया है), लेकिन क्लाइंट-साइड के बारे में क्या? मैं ओपी के समान स्थिति में हूं, लेकिन यह पता नहीं लगा सकता कि क्लाइंट-साइड सत्यापन इंजन (तीन मान्यताओं के बजाय - प्रत्येक फ़ील्ड के लिए एक) में नए नियम में कैसे जुड़ना है। विचार? – elsurudo

+0

हाँ, क्लाइंट साइड सत्यापन को विस्तारित करना थोड़ा जटिल है, लेकिन संभव है। जो मैं मानता हूं वह एक यूआई का निर्माण कर रहा है जो उपयोगकर्ताओं को अमान्य मानों (स्पिनरों या स्लाइडर्स का उपयोग करके) में प्रवेश करना मुश्किल बनाता है, और फिर सर्वर पर किनारे के मामलों को पकड़ता है। – OdeToCode

0

http://www.asp.net/mvc साइट पर Contact Manager नमूना आवेदन अपने नियंत्रक और मॉडल से एक सेवा परत में अपने मान्यता तर्क को अलग करने का एक बहुत अच्छा विवरण नहीं है।

यह अच्छी तरह से काम कर रहा है एक पढ़ा

+0

हाँ मैंने ऐप देखा है। यह मेरे द्वारा उठाए गए विशेष प्रश्न के लिए कुछ भी नहीं करता है। मेरी समस्या यह है कि मॉडल में डेटटाइम फ़ील्ड है। हालांकि, यह फ़ॉर्म इसे 3 टेक्स्टबॉक्स के रूप में दर्शाता है। इसलिए, मॉडल को भी बाध्य होने से पहले मुझे सत्यापन के कुछ रूप करना चाहिए, और मैं ऐसा करने का सबसे अच्छा तरीका समझने की कोशिश कर रहा हूं। – Chris

+1

इस पोस्ट पर एक नज़र डालें: http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx – Charlino

1

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

ऐसे xVal के रूप में एक प्रमाणीकरण फ्रेमवर्क इस बहुत सरल बना देता है।

2

मैं दूसरे दिन एक ही सटीक स्थिति थी ... नीचे अपने मॉडल बंधन कोड है। असल में यह सभी डेटटाइम बांधता है? एक मॉडल के फ़ील्ड महीने/दिन/वर्ष के फ़ील्ड को किसी फॉर्म से (यदि संभव हो) तो, हाँ, मैं यहां सत्यापन में जोड़ता हूं, क्योंकि ऐसा करने के लिए उचित लगता है।

public class DateModelBinder : DefaultModelBinder 
    { 

     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
     { 

      if (propertyDescriptor.PropertyType == typeof(DateTime?)) 
      { 
       string DateMonth = _GetDateValue(bindingContext, propertyDescriptor.Name + "Month"); 
       string DateDay = _GetDateValue(bindingContext, propertyDescriptor.Name + "Day"); 
       string DateYear = _GetDateValue(bindingContext, propertyDescriptor.Name + "Year"); 
       // Try to parse the date if we have at least a month, day or year 
       if (!String.IsNullOrEmpty(DateMonth) || !String.IsNullOrEmpty(DateDay) || !String.IsNullOrEmpty(DateYear)) 
       { 
        DateTime fullDate; 
        CultureInfo enUS = new CultureInfo("en-US"); 
        // If we can parse it, set the model property 
        if (DateTime.TryParse(DateMonth + "/" + DateDay + "/" + DateYear, 
             enUS, 
             DateTimeStyles.None, out fullDate)) 
        { 
         SetProperty(controllerContext, bindingContext, propertyDescriptor, (DateTime?)fullDate); 
        } 
        // The date is invalid, so we need to add a model error 
        else 
        { 
         string ModelPropertyName = bindingContext.ModelName; 
         if(ModelPropertyName != "") 
         { 
          ModelPropertyName += "."; 
         } 
         ModelPropertyName += propertyDescriptor.Name; 
         bindingContext.ModelState.AddModelError(ModelPropertyName, "Invalid date supplied for " + propertyDescriptor.Name); 
        } 
       } 
       return; 
      } 
      base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
     } 

     // Get a property from binding context 
     private string _GetDateValue(ModelBindingContext bindingContext, string key) 
     { 
      ValueProviderResult valueResult; 
      bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName + "." + key, out valueResult); 
      //Didn't work? Try without the prefix if needed... 
      // && bindingContext.FallbackToEmptyPrefix == true 
      if (valueResult == null) 
      { 
       bindingContext.ValueProvider.TryGetValue(key, out valueResult); 
      } 
      if (valueResult == null) 
      { 
       return null; 
      } 
      return (string)valueResult.ConvertTo(typeof(string)); 
     } 

    } 

नोट: मैं bindingContext.FallbackToEmptyPrefix हमेशा गलत होने के साथ कुछ समस्या ... उस पर कोई उपयोगी जानकारी नहीं मिल सकता है नहीं था, लेकिन आप विचार मिलता है।

+0

स्कॉट हंसेलमैन ब्लॉग पोस्ट से कोड की कॉपी/अतीत की तरह दिखता है: http://www.hanselman.com/blog /SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx – JMP

4

कभी कभी मॉडल एक दृश्य मॉडल, नहीं एक डोमेन मॉडल है। इस मामले में आप उन दोनों को अलग करने और दृश्य दृश्य को अपने दृश्य से मेल खाने के लिए डिज़ाइन कर सकते हैं।

अब आप दृश्य मॉडल इनपुट को मान्य है और एक DateTime में तीन क्षेत्रों को पार्स कर सकते हैं। फिर यह डोमेन मॉडल अपडेट कर सकता है:

public ActionResult SomeAction(ViewModel vm) 
{ 
    if (vm.IsValid) 
    { 
     var dm = repositoryOrSomething.GetDomainModel(); 
     vm.Update(dm); 
    } 

    // more code... 
} 
0

मैं छोटे छोटे उद्देश्य वाले व्यूमोडल्स बनाने से थक गया जो केवल मेरे मील-व्यापी डोमेन मॉडल के हिस्सों को छुआ।

इस प्रकार, मैंने इसे संबोधित करने के लिए अपना स्वयं का तरीका पकाया। मेरा व्यूमोडेल एक प्रकार का डोमेन मॉडल है, और मैं इसकी पहचान गुणों को पहले लोड करने के लिए custom model binder का उपयोग करता हूं - पहचान पहचानने के बाद - यह एक डोमेन मॉडल को लोड करता है। लोड, और शेष बाध्यकारी गतिविधि अनिवार्य रूप से 'विलय' करती है।

फिर, जब मेरा व्यूमोडेल बाध्य होता है (उदा। फॉर्म पोस्ट पर), आईडी के साथ आवश्यक फ़ील्ड सेट होने के बाद - यह तुरंत डेटाबेस से डोमेन मॉडल को लोड करता है। मुझे बस डिफॉल्टमोडेल बाइंडर के लिए एक प्रतिलिपि के साथ आना पड़ा। मेरा कस्टम मॉडल बाइंडर posted here on StackOverflow आपको गुणों के बाध्यकारी क्रम को नियंत्रित करने की अनुमति देता है।

एक बार मुझे लगता है कि पहचान गुण बाध्य कर रहे हैं, (मेरे viewmodel के आंतरिक पहचान setters के पूरा करने के लिए सुनो) मैं अपने डोमेन मॉडल का भार को गति प्रदान के रूप में गुण के बाकी बाध्य कर रहे हैं गारंटी ले सकते हैं, वे अधिलेखित कर रहे हैं, यानी लोड किए गए डोमेन मॉडल में 'विलय'।

असल में, मेरे पास मेरे सभी विभिन्न रेजर विचार हो सकते हैं, चाहे वे 5 फॉर्म फ़ील्ड या मॉडल के 50 फ़ील्ड का पर्दाफाश कर सकें .. सभी इस तरह के एक नियंत्रक कार्रवाई को सबमिट करते हैं (दिया गया है, मैं अभी भी अलग-अलग क्रियाएं करता हूं जहां आवश्यक है उचित कस्टम व्यापार सामान करो ..लेकिन मुद्दा यह है कि, मेरे नियंत्रक क्रियाएं केंद्रित और संक्षेप में हैं)

<HttpPost()> 
<Authorize(Roles:="MYCOMPANY\activeDirRoleForEditing")> 
Function Edit(<Http.FromBody()> ByVal mergedModel As OrderModel) As ActionResult 
    'notice: NO loading logic here - it already happened during model binding 
    'just do the right thing based upon resulting model state 
    If Me.ModelState.IsValid Then 

     mergedModel.SaveAndReload("MyServiceWebConfigKey") 

     ViewBag.SuccessMessage = String.Format("You have successfully edited the order {0}", mergedModel.Id) 

     Return View("Edit", mergedModel) 
    Else 
     ViewBag.ErrorText = String.Format("Order {0} not saved. Check for errors and correct.", mergedModel.Id) 
     Return View("Edit", mergedModel) 
    End If 
End Function 
संबंधित मुद्दे