2011-06-27 16 views
7

मैं समझता हूं कि एमवीसी में अलग-अलग चिंताओं के लिए "उचित" संरचना आपके चुने हुए भंडार में बने रहने के लिए आपके विचारों और अलग-अलग डेटा-मॉडलों के निर्माण के लिए दृश्य-मॉडल होना है। मैंने मोंगोडीबी के साथ प्रयोग करना शुरू कर दिया और मुझे लगता है कि स्कीमा-कम, नो-एसक्यूएल स्टाइल डेटाबेस का उपयोग करते समय यह लागू नहीं हो सकता है। मैं इस परिदृश्य को स्टैक ओवरफ्लो समुदाय में पेश करना चाहता था और देख सकता हूं कि हर किसी के विचार क्या हैं। मैं एमवीसी के लिए नया हूं, इसलिए यह मुझे समझ में आया, लेकिन शायद मैं कुछ देख रहा हूं ...एमवीसी और एनओएसक्यूएल: दृश्य मॉडलों को सीधे मोंगोडीबी में सहेजना?

इस चर्चा के लिए मेरा उदाहरण यहां दिया गया है: जब कोई उपयोगकर्ता अपनी प्रोफ़ाइल संपादित करना चाहता है, तो वे UserEdit पर जाएंगे देखें, जो नीचे UserEdit मॉडल का उपयोग करता है।

public class UserEditModel 
{ 
    public string Username 
    { 
     get { return Info.Username; } 
     set { Info.Username = value; } 
    } 

    [Required] 
    [MembershipPassword] 
    [DataType(DataType.Password)] 
    public string Password { get; set; } 

    [DataType(DataType.Password)] 
    [DisplayName("Confirm Password")] 
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 
    public string ConfirmPassword { get; set; } 

    [Required] 
    [Email] 
    public string Email { get; set; } 

    public UserInfo Info { get; set; } 
    public Dictionary<string, bool> Roles { get; set; } 
} 

public class UserInfo : IRepoData 
{ 
    [ScaffoldColumn(false)] 
    public Guid _id { get; set; } 

    [ScaffoldColumn(false)] 
    public DateTime Timestamp { get; set; } 

    [Required] 
    [DisplayName("Username")] 
    [ScaffoldColumn(false)] 
    public string Username { get; set; } 

    [Required] 
    [DisplayName("First Name")] 
    public string FirstName { get; set; } 

    [Required] 
    [DisplayName("Last Name")] 
    public string LastName { get; set; } 

    [ScaffoldColumn(false)] 
    public string Theme { get; set; } 

    [ScaffoldColumn(false)] 
    public bool IsADUser { get; set; } 
} 

ध्यान दें कि UserEditModel क्लास में UserInfo का एक उदाहरण है जो आईरिपोडाटा से प्राप्त होता है? UserInfo डेटाबेस में सहेजा जाता है। मेरे पास एक सामान्य भंडार वर्ग है जो किसी भी वस्तु को स्वीकार करता है जो विरासत में आईआरपीओडेटा बनाता है और इसे बचाता है; तो मैं बस Repository.Save(myUserInfo) पर कॉल करता हूं और यह किया जाता है। आईरिपोडाटा _id (मोंगोडीबी नामकरण सम्मेलन) और एक टाइमस्टैम्प को परिभाषित करता है, इसलिए भंडार _id के आधार पर अपरिवर्तित हो सकता है और टिमस्टैम्प के आधार पर संघर्षों की जांच कर सकता है, और ऑब्जेक्ट जो भी अन्य गुणों को मोंगोडीबी में सहेजा गया है। अधिकांश भाग के लिए दृश्य को केवल @Html.EditorFor का उपयोग करने की आवश्यकता है और हम जाने के लिए अच्छे हैं! असल में, जो कुछ भी दृश्य दृश्यों को बेस-मॉडल में जाता है, कुछ भी जो केवल भंडार की जरूरत है, केवल [ScaffoldColumn(false)] एनोटेशन प्राप्त करता है, और बाकी सब कुछ दोनों के बीच आम है। (Btw - उपयोगकर्ता नाम, पासवर्ड, भूमिका, और ईमेल, .NET प्रदाताओं के लिए सहेज इतना है कि क्यों वे UserInfo वस्तु में नहीं हैं।)

बड़ा लाभ इस परिदृश्य के दो गुना कर रहे हैं ...

  1. मैं कम कोड है, जो इसलिए अधिक आसानी से समझा है, तेजी से विकसित करने के लिए, और अधिक maintainable है (मेरी राय में) का उपयोग कर सकते हैं।

  2. मैं फिर से कारक सेकंड में ... मैं एक दूसरे ईमेल पता जोड़ने की जरूरत है, मैं सिर्फ यह UserInfo वस्तु में जोड़ने - यह दृश्य में जोड़ा और भंडार करने के लिए सहेजा जाता है बस जोड़कर वस्तु के लिए एक संपत्ति। क्योंकि मैं MongoDB का उपयोग कर रहा हूं, मुझे किसी भी मौजूदा डेटा के साथ मेरी डीबी स्कीमा या गड़बड़ को बदलने की आवश्यकता नहीं है।

इस सेटअप को देखते हुए, डेटा संग्रह करने के लिए अलग-अलग मॉडल बनाने की आवश्यकता है? आप सभी को क्या लगता है कि इस दृष्टिकोण के नुकसान हैं? मुझे एहसास है कि स्पष्ट उत्तर मानकों और अलगाव की चिंताओं हैं, लेकिन क्या कोई वास्तविक दुनिया उदाहरण हैं जो आप सोच सकते हैं कि इससे कुछ सिरदर्द पैदा होंगे?

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

उत्तर

9

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

बड़ी चीजों में से एक डेटा मॉडलिंग/दृढ़ता के लिए एक ही कक्षा का उपयोग करने के साथ व्यापार तर्क/डेटा अखंडता चिंताओं के रूप में आप विचारों में उपयोग करते हैं। उस स्थिति को लें जहां आपके उपयोगकर्ता वर्ग में DateTime DateAdded संपत्ति है, जो उपयोगकर्ता को जोड़े जाने पर इंगित करने के लिए है।

[HttpPost] 
public ActionResult Edit(UserInfo model) { } 

सबसे अधिक संभावना आप नहीं उपयोगकर्ता को बदलने में सक्षम होना चाहता हूँ, जब वे किया गया था: आप एक रूप है कि आपके UserInfo वर्ग आप एक कार्रवाई हैंडलर ऐसा दिखता है जैसे के साथ अंत में सीधे हुक प्रदान करते हैं प्रणाली, तो आपका पहला विचार फॉर्म में एक फ़ील्ड प्रदान नहीं करना है।

हालांकि, आप दो कारणों से उस पर भरोसा नहीं कर सकते हैं। सबसे पहले यह है कि DateAdded के लिए मूल्य वही होगा जैसा आपने new DateTime() किया था या यह null होगा (या तो इस उपयोगकर्ता के लिए गलत होगा)।

इसके साथ दूसरा मुद्दा यह है कि उपयोगकर्ता फॉर्म अनुरोध में इसे धोखा दे सकते हैं और PO12 डेटा में &DateAdded=<whatever date> जोड़ सकते हैं, और अब आपका एप्लिकेशन डीबी में डेटएड फ़ील्ड को जो कुछ भी दर्ज किया गया है, उसे बदल देगा।

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

व्यूमोडल्स में यह समस्या नहीं है क्योंकि आपके व्यू मॉडल को डेटा इकाई से स्वयं को परिवर्तित करने के बारे में पता होना चाहिए, और इसमें DateAdded फ़ील्ड को धोखा देने के लिए नहीं है, इसमें केवल न्यूनतम फ़ील्ड हैं जिन्हें प्रदर्शित करने की आवश्यकता है (या प्राप्त) यह डेटा है।

आपके सटीक परिदृश्य में, मैं इसे POST स्ट्रिंग मैनिपुलेशन के साथ आसानी से पुन: उत्पन्न कर सकता हूं, क्योंकि आपके व्यू मॉडल में आपकी डेटा इकाई तक सीधे पहुंच है।

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

public DateTime? BannedDate { get; set; } 
public DateTime? ActivationDate { get; set; } // Date the account was activated via email link 
अब

के एक व्यवस्थापक आप सभी उपयोगकर्ताओं की स्थिति पर रुचि रखते हैं के रूप में कहते हैं, और आप एक स्थिति संदेश के रूप में प्रत्येक उपयोगकर्ता के आगे प्रदर्शित करना चाहते हैं अच्छी तरह से व्यवस्थापक को उस उपयोगकर्ता की स्थिति के आधार पर विभिन्न क्रियाएं दे सकते हैं। आप अपने डेटा मॉडल का उपयोग करते हैं, अपने दृश्य के कोड दिखेगा की तरह: क्योंकि अब आप अपने विचारों में व्यापार तर्क का एक बहुत (प्रतिबंध लगा दिया के उपयोगकर्ता स्थिति हमेशा सक्रिय उपयोगकर्ताओं पर प्रतिबंध लगा दिया उन पर पूर्वता लेता है

// In status column of the web page's data grid 

@if (user.BannedDate != null) 
{ 
    <span class="banned">Banned</span> 
} 
else if (user.ActivationDate != null) 
{ 
    <span class="Activated">Activated</span> 
} 

//.... Do some html to finish other columns in the table 
// In the Actions column of the web page's data grid 
@if (user.BannedDate != null) 
{ 
    // .. Add buttons for banned users 
} 
else if (user.ActivationDate != null) 
{ 
    // .. Add buttons for activated users 
} 

यह बुरा है उपयोगकर्ताओं द्वारा प्रतिबंधित तारीख, आदि के साथ परिभाषित किया गया है ...)। यह भी बहुत जटिल है।

इसके बजाए, बेहतर (imho कम से कम) समाधान आपके उपयोगकर्ताओं को एक व्यूमोडेल में लपेटना है, जिसमें उनकी स्थिति के लिए गणना है, और जब आप अपने मॉडल को अपने दृश्य मॉडल में परिवर्तित करते हैं (दृश्य मॉडल का कन्स्ट्रक्टर एक अच्छी जगह है ऐसा करें) आप सभी तिथियों को देखने के लिए एक बार अपना व्यावसायिक तर्क डाल सकते हैं और पता लगा सकते हैं कि उपयोगकर्ता किस स्थिति में होना चाहिए।

फिर ऊपर अपने कोड के रूप में सरल है:

// In status column of the web page's data grid 

@if (user.Status == UserStatuses.Banned) 
{ 
    <span class="banned">Banned</span> 
} 
else if (user.Status == UserStatuses.Activated) 
{ 
    <span class="Activated">Activated</span> 
} 

//.... Do some html to finish other columns in the table 
// In the Actions column of the web page's data grid 
@if (user.Status == UserStatuses.Banned) 
{ 
    // .. Add buttons for banned users 
} 
else if (user.Status == UserStatuses.Activated) 
{ 
    // .. Add buttons for activated users 
} 

कौन इस सरल परिदृश्य में कम कोड की तरह नहीं हो सकता है, लेकिन यह चीजों को एक बहुत अधिक पोषणीय जब कोई उपयोगकर्ता के लिए एक स्थिति का निर्धारण करने के लिए तर्क हो जाता है अधिक जटिल। अब आप अपने डेटा मॉडल को बदलने के बिना उपयोगकर्ता की स्थिति को निर्धारित करने के तर्क को बदल सकते हैं (आपको डेटा देखने के तरीके के कारण अपना डेटा मॉडल बदलना नहीं चाहिए) और यह एक स्थान पर स्थिति निर्धारण को बनाए रखता है।

+0

मैं मूल रूप से सहमत हूं। पांचवें अनुच्छेद में, शायद आपको यह इंगित करना चाहिए कि मॉडल बाध्यकारी समस्या का कारण है (यानी सही नाम वाले प्रत्येक फ़ील्ड को बाध्य किया जाएगा), और मॉडल बाध्यकारी को कुछ क्षेत्रों को अनदेखा करने के लिए भी कॉन्फ़िगर किया जा सकता है - भले ही मैं मानूं वह खतरनाक है। चूंकि मैपिंग कोड उबाऊ और त्रुटि-प्रवण है, इसलिए ऑटोमैपर को ViewModels से और उसके लिए मानचित्र करना अच्छा विचार हो सकता है। – mnemosyn

+0

आह अच्छा बिंदु, मैं इसे जोड़ दूंगा। और मैं इस बात से सहमत हूं कि ऑटोमैपर शायद इसे मैन्युअल रूप से करने के बजाय दृश्य मॉडल में कनवर्ट करने का बेहतर समाधान है। – KallDrexx

+1

वाह, मेरे लिए उस सुप्रसिद्ध उत्तर को बनाने के लिए समय निकालने के लिए धन्यवाद! – jrizzo

7

tl; डॉ

वहाँ एक आवेदन में मॉडल की कम से कम 3 परतों, कभी कभी वे सुरक्षित रूप से जोड़ा जा सकता है, कभी कभी नहीं कर रहे हैं। प्रश्न के संदर्भ में, दृढ़ता और डोमेन मॉडल को जोड़ना ठीक है, लेकिन दृश्य मॉडल नहीं।

पूर्ण पोस्ट

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

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

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

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

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

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

अन्त में अपने अंक के बारे में:

  1. कम कोड केवल एक फायदा है जब यह वास्तव में अधिक समझा जा सकता है है। इसकी पढ़ने योग्यता और समझने योग्य व्यक्ति इसे लिखने वाले व्यक्ति के कौशल के परिणाम हैं। संक्षिप्त कोड के प्रसिद्ध उदाहरण हैं जिन्होंने ठोस डेवलपर्स को विच्छेदन और समझने में काफी समय लगाया है। उनमें से अधिकतर उदाहरण चालाकी से लिखे गए कोड से आते हैं जो अधिक समझ में नहीं आता है। अधिक महत्वपूर्ण यह है कि आपका कोड 100% आपके विनिर्देश को पूरा करता है। यदि आपका कोड छोटा है, आसानी से समझा जा सकता है और पठनीय है लेकिन विनिर्देश को पूरा नहीं करता है, तो यह बेकार है। यदि यह उन सभी चीजों में से है और विनिर्देश को पूरा करता है, लेकिन आसानी से शोषण योग्य है, तो विनिर्देश और कोड बेकार है।

  2. सेकंड में सुरक्षित रूप से रिफैक्टरिंग अच्छी तरह से लिखित कोड का परिणाम है, न कि यह terseness है। डीआरवाई सिद्धांत के बाद आपका कोड आसानी से अपरिवर्तनीय बना देगा जब तक कि आपका विनिर्देश सही ढंग से आपके लक्ष्यों को पूरा न करे। मॉडल परतों के मामले में, आपका डोमेन मॉडल अच्छा, रखरखाव करने योग्य और कोड को रीफैक्टर करने में आसान लिखने की कुंजी है। आपका डोमेन मॉडल उस गति से बदल जाएगा जिस पर आपकी व्यावसायिक आवश्यकताएं बदलती हैं। आपकी व्यावसायिक आवश्यकताओं में परिवर्तन बड़े बदलाव हैं, और यह सुनिश्चित करने के लिए सावधानी बरतनी है कि एक नया नमूना पूरी तरह से सोचा गया है, डिज़ाइन किया गया है, कार्यान्वित किया गया है, परीक्षण किया गया है। उदाहरण के लिए आप आज कहते हैं कि आप दूसरा ईमेल पता जोड़ना चाहते हैं। आपको अभी भी दृश्य को बदलना होगा (जब तक कि आप किसी प्रकार का मचान नहीं कर रहे हों)। साथ ही, अगर कल आपको 100 ईमेल पते के लिए समर्थन जोड़ने के लिए आवश्यकताएं बदलती हैं तो क्या होगा? आपके द्वारा मूल रूप से प्रस्तावित परिवर्तन किसी भी प्रणाली के लिए आसान था, बड़े बदलावों के लिए अधिक काम की आवश्यकता होती है।

संबंधित मुद्दे