2011-04-21 11 views
22

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

public class Transport : IEntity<Transport> 
{ 
    private readonly TransportID transportID; 
    private readonly PersonCapacity personCapacity; 

    public Transport(TransportID transportID,PersonCapacity personCapacity) 
    { 
     Validate.NotNull(personCapacity, "personCapacity is required"); 
     Validate.NotNull(transportID, "transportID is required"); 

     this.transportID = transportID; 
     this.personCapacity = personCapacity; 
    } 

    public virtual PersonCapacity PersonCapacity 
    { 
     get { return personCapacity; } 
    } 

    public virtual TransportID TransportID 
    { 
     get { return transportID; } 
    } 
} 


public class TransportID:IValueObject<TransportID> 
{ 
    private readonly string number; 

    #region Constr 

    public TransportID(string number) 
    { 
     Validate.NotNull(number); 

     this.number = number; 
    } 

    #endregion 

    public string IdString 
    { 
     get { return number; } 
    } 
} 

public class PersonCapacity:IValueObject<PersonCapacity> 
{ 
    private readonly int numberOfSeats; 

    #region Constr 

    public PersonCapacity(int numberOfSeats) 
    { 
     Validate.NotNull(numberOfSeats); 

     this.numberOfSeats = numberOfSeats; 
    } 

    #endregion 

    public int NumberOfSeats 
    { 
     get { return numberOfSeats; } 
    } 
} 

स्पष्ट रूप से स्वैपिंग यहां काम नहीं करती है। अब मैं उन तीन वर्गों को BsonClassMaps के माध्यम से हाथ से मैप कर सकता हूं और वे बस ठीक से संग्रहीत किए जाएंगे। समस्या यह है कि, जब मैं उन्हें डीबी से लोड करना चाहता हूं तो मुझे उन्हें BsonDocuments के रूप में लोड करना होगा, और उन्हें अपने डोमेन ऑब्जेक्ट में पार्स करना होगा। मैंने बहुत सी चीजों की कोशिश की लेकिन आखिरकार एक साफ समाधान पाने में असफल रहा। क्या मुझे वास्तव में मोंगोडीबी के लिए सार्वजनिक गेटर्स/सेटर्स के साथ डीटीओ का उत्पादन करना है और उनको अपने डोमेन ऑब्जेक्ट्स पर मैप करना है? शायद कोई मुझे इस पर कुछ सलाह दे सकता है।

उत्तर

14

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

BsonClassMap.RegisterClassMap<C>(cm => { 
    cm.MapIdField("id"); 
    cm.MapField("x"); 
}); 

ध्यान दें कि निजी क्षेत्रों केवल पढ़ने के लिए नहीं किया जा सकता:

public class C { 
    private ObjectId id; 
    private int x; 

    public C(ObjectId id, int x) { 
     this.id = id; 
     this.x = x; 
    } 

    public ObjectId Id { get { return id; } } 
    public int X { get { return x; } } 
} 

निम्नलिखित प्रवर्तन कोड का उपयोग कर मैप किया जा सकता है। ध्यान दें कि deserialization आपके कन्स्ट्रक्टर को छोड़ देता है और सीधे निजी फ़ील्ड शुरू करता है (.NET serialization इस तरह से भी काम करता है)।

http://www.pastie.org/1822994

+0

यह काम करता है! मुझे विश्वास नहीं है कि मुझे यह याद आया। मुझे लगता है कि इतने बड़े पैमाने पर अनुप्रयोगों के लिए, निजी क्षेत्रों से "पठनीय" को हटाने से डीटीओ की अतिरिक्त परत नहीं बनने के लिए स्वीकार्य ट्रेडऑफ है। जैसा कि आपने बताया है, किसी को ctor को छोड़कर अमान्य वस्तुओं को बनाने के लिए सावधान रहना होगा। फिर भी, मैं ब्रायन और नील्स को उनके उत्तरों के लिए भी धन्यवाद देना चाहता हूं। आप सभी ने मुझे थोड़ा चालाक बना दिया, धन्यवाद। – hoetz

+2

छोटा बिंदु: पाठक क्षेत्र को हटाकर इसे 'सार्वजनिक ऑब्जेक्ट आईडी आईडी {प्राप्त करें; निजी सेट; } 'ऑटो-प्रॉपर्टी और फ़ील्ड को पूरी तरह से हटाया जा सकता है। –

+1

हालांकि यह एक संभावित समाधान है, ** यह अभी भी आपके मॉडल ** को प्रभावित करता है, जो कि आपके द्वारा दिए गए ठोस उदाहरण में फ़ील्ड की आवश्यकता है जिसे 'रीडोनली' नहीं किया जाता है। प्रत्येक टीम को खुद को तय करना होगा कि यह उनके प्रोजेक्ट के लिए स्वीकार्य है या नहीं। – theDmi

3

मैं बीएसओएन दस्तावेज़ों को पार्स करने और पार्सिंग तर्क को कारखाने में ले जाने के साथ जाऊंगा।

पहले एक फैक्टरी बेस क्लास को परिभाषित करें, जिसमें बिल्डर क्लास शामिल है। बिल्डर वर्ग डीटीओ के रूप में कार्य करेगा, लेकिन डोमेन ऑब्जेक्ट बनाने से पहले मूल्यों के अतिरिक्त सत्यापन के साथ।

public class TransportFactory<TSource> 
{ 
    public Transport Create(TSource source) 
    { 
     return Create(source, new TransportBuilder()); 
    } 

    protected abstract Transport Create(TSource source, TransportBuilder builder); 

    protected class TransportBuilder 
    { 
     private TransportId transportId; 
     private PersonCapacity personCapacity; 

     internal TransportBuilder() 
     { 
     } 

     public TransportBuilder WithTransportId(TransportId value) 
     { 
      this.transportId = value; 

      return this; 
     } 

     public TransportBuilder WithPersonCapacity(PersonCapacity value) 
     { 
      this.personCapacity = value; 

      return this; 
     } 

     public Transport Build() 
     { 
      // TODO: Validate the builder's fields before constructing. 

      return new Transport(this.transportId, this.personCapacity); 
     } 
    } 
} 

अब, अपने भंडार में एक फैक्ट्री सबक्लास बनाएं। यह कारखाना बीएसओएन दस्तावेजों से डोमेन ऑब्जेक्ट्स का निर्माण करेगा।

public class TransportRepository 
{ 
    public Transport GetMostPopularTransport() 
    { 
     // Query MongoDB for the BSON document. 
     BsonDocument transportDocument = mongo.Query(...); 

     return TransportFactory.Instance.Create(transportDocument); 
    } 

    private class TransportFactory : TransportFactory<BsonDocument> 
    { 
     public static readonly TransportFactory Instance = new TransportFactory(); 

     protected override Transport Create(BsonDocument source, TransportBuilder builder) 
     { 
      return builder 
       .WithTransportId(new TransportId(source.GetString("transportId"))) 
       .WithPersonCapacity(new PersonCapacity(source.GetInt("personCapacity"))) 
       .Build(); 
     } 
    } 
} 

इस दृष्टिकोण के फायदे:

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

    public class TransportWebServiceWrapper 
    { 
        private class TransportFactory : TransportFactory<XDocument> 
        { 
         protected override Transport Create(XDocument source, TransportBuilder builder) 
         { 
          // Construct domain object from XML. 
         } 
        } 
    } 
    
  • स्रोत डेटा की पार्स तर्क, डेटा का मूल स्रोत के करीब है पार्स यानी: उदाहरण के लिए, यदि आप एक वेब सेवा है कि XML रिटर्न के साथ इंटरफेस करने की जरूरत है, तो आप सिर्फ एक नया कारखाना उपवर्ग बनाने बीएसओएन दस्तावेज़ों में भंडार में है, एक्सएमएल का पार्सिंग वेब सेवा रैपर में है। यह संबंधित तर्क को एक साथ समूहीकृत रखता है।

कुछ नुकसान:

  • मैं अभी तक बड़े और जटिल परियोजनाओं में इस दृष्टिकोण प्रयास नहीं किया है, केवल छोटे पैमाने पर परियोजनाओं में। कुछ परिदृश्यों में कुछ कठिनाइयों हो सकती हैं जिन्हें मैंने अभी तक नहीं सामना किया है।
  • यह कुछ सामान्य रूप से सरल के लिए कुछ कोड है। खासकर बिल्डर्स काफी बड़े हो सकते हैं।आप सरल गुणों के लिए सभी WithXxx() विधियों को परिवर्तित करके बिल्डरों में कोड की मात्रा को कम कर सकते हैं।
+0

बहुत ही रोचक अवधारणा, मुझे लगता है कि एक परत जोड़ने की तुलना में वास्तव में कोई अन्य तरीका नहीं है। इवांस की किताब के सी # डीडीडी डेमो एप्लिकेशन में, उन्होंने एनएचबर्ननेट का इस्तेमाल किया और मुझे बस ऐसा करने की अवधारणा पसंद नहीं है।आप अपने अपरिवर्तित डोमेन ऑब्जेक्ट्स में फेंक देते हैं और आप बिना किसी अतिरिक्त कक्षा के उन्हें वापस ले जाते हैं (पाठ्यक्रम की एक्सएमएल मैपिंग को छोड़कर) – hoetz

-1

नील्स एक दिलचस्प समाधान है, लेकिन मैं एक बहुत अलग दृष्टिकोण का प्रस्ताव: अपने डेटा मॉडल को सरल बनाएँ।

मैं यह कहता हूं क्योंकि आप आरडीबीएमएस शैली की इकाइयों को मोंगोडीबी में परिवर्तित करने की कोशिश कर रहे हैं और यह आपके जैसा पाया गया है, यह बहुत अच्छी तरह से मानचित्र नहीं है।

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

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

+4

लेकिन डीडीडी के साथ, मुझे दृढ़ता से चिंता करने की ज़रूरत नहीं है। मैं अपने डोमेन इकाइयों को एक निश्चित स्टोरेज तकनीक के साथ दिमाग में डिजाइन नहीं करता हूं। – hoetz

+0

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

+0

@Niels के साथ और अधिक सहमत नहीं हो सका। लेकिन मुझे लगता है कि यह उल्लेखनीय है कि सिद्धांत में जबकि आपको अपनी दृढ़ता परत के बारे में चिंता करने की ज़रूरत नहीं है, असली दुनिया में, इसके बारे में सोचने के लिए निश्चित रूप से कुछ - विशेष रूप से जब कोई नोएसक्यूएल समाधान पर विचार करते हैं। –

0

पर विचार के आदर्श, सी # में MongoDB के लिए एक खुला स्रोत ORM:

यहाँ एक पूर्ण नमूना कार्यक्रम है कि इस परीक्षण है।

ये कुछ लिंक कर रहे हैं:

http://www.codevoyeur.com/Articles/20/A-NoRM-MongoDB-Repository-Base-Class.aspx

http://lukencode.com/2010/07/09/getting-started-with-mongodb-and-norm/

https://github.com/atheken/NoRM (डाउनलोड)

2

अब इस से निपटने के लिए एक बेहतर दृष्टिकोण MapCreator उपयोग कर रहा है (जो संभवतः इनमें से अधिकांश के बाद जोड़ा गया है जवाब लिखे गए थे)।

उदा। मेरे पास Time नामक एक कक्षा है जिसमें तीन पाठक गुण हैं: Hour, Minute और Second। यहां बताया गया है कि मैं डेटाबेस में उन तीन मानों को संग्रहीत करने और deserialization के दौरान नई Time वस्तुओं को बनाने के लिए कैसे मिलता हूं।

BsonClassMap.RegisterClassMap<Time>(cm => 
{ 
    cm.AutoMap(); 
    cm.MapCreator(p => new Time(p.Hour, p.Minute, p.Second)); 
    cm.MapProperty(p => p.Hour); 
    cm.MapProperty(p => p.Minute); 
    cm.MapProperty(p => p.Second); 
} 
+2

यदि आप सभी गुण स्पष्ट रूप से मैप किए गए हैं तो आप 'ऑटोमैप()' का उपयोग क्यों करते हैं? –