2011-11-12 8 views
8

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

मेरी समस्या हालांकि: मैं एक दूसरे से संबंधित संस्थाओं के साथ साफ तरीके से कैसे निपटूंगा, और फिर वास्तविक उद्देश्य के बिना आईरिपॉजिटरी नहीं है?

चलो कहते हैं कि मैं इन व्यापार वस्तुओं करते हैं:

public class Region { 
    public Guid InternalId {get; set;} 
    public string Name {get; set;} 
    public ICollection<Location> Locations {get; set;} 
    public Location DefaultLocation {get; set;} 
} 

public class Location { 
    public Guid InternalId {get; set;} 
    public string Name {get; set;} 
    public Guid RegionId {get; set;} 
} 

हैं नियम:

  • हर क्षेत्र है चाहिए कम से कम एक स्थान
  • नव निर्मित क्षेत्र एक स्थान के साथ बनाया जाता
  • कोई चयन एन + 1 कृपया

तो मेरा क्षेत्र रिपोजिटरी कैसा दिखता है?

public class RegionRepository : IRepository<Region> 
{ 
    // Linq To Sql, injected through constructor 
    private Func<DataContext> _l2sfactory; 

    public ICollection<Region> GetAll(){ 
     using(var db = _l2sfactory()) { 
      return db.GetTable<DbRegion>() 
         .Select(dbr => MapDbObject(dbr)) 
         .ToList(); 
     } 
    } 

    private Region MapDbObject(DbRegion dbRegion) { 
     if(dbRegion == null) return null; 

     return new Region { 
      InternalId = dbRegion.ID, 
      Name = dbRegion.Name, 
      // Locations is EntitySet<DbLocation> 
      Locations = dbRegion.Locations.Select(loc => MapLoc(loc)).ToList(), 
      // DefaultLocation is EntityRef<DbLocation> 
      DefaultLocation = MapLoc(dbRegion.DefaultLocation) 
     } 
    } 

    private Location MapLoc(DbLocation dbLocation) { 
     // Where should this come from? 
    } 
} 

तो जैसा कि आप देखते हैं, एक क्षेत्रीय भंडार को भी स्थानों को लाने की आवश्यकता है। मेरे उदाहरण में, मैं लिंक टू एसक्यूएल एंटिटीसेट/EntiryRef का उपयोग करता हूं, लेकिन अब क्षेत्र को व्यापार ऑब्जेक्ट्स में स्थान मैपिंग के साथ सौदा करने की आवश्यकता है (क्योंकि मेरे पास ऑब्जेक्ट्स, बिजनेस और एल 2 एस ऑब्जेक्ट्स के दो सेट हैं)।

public class RegionRepository : IRepository<Region> 
{ 
    private IRepository<Location> _locationRepo; 

    // snip 

    private Region MapDbObject(DbRegion dbRegion) { 
     if(dbRegion == null) return null; 

     return new Region { 
      InternalId = dbRegion.ID, 
      Name = dbRegion.Name, 
      // Now, LocationRepo needs to concern itself with Regions... 
      Locations = _locationRepo.GetAllForRegion(dbRegion.ID), 
      // DefaultLocation is a uniqueidentifier 
      DefaultLocation = _locationRepo.Get(dbRegion.DefaultLocationId) 
     } 
    } 

अब मैं अच्छी तरह से परमाणु खजाने में अपने डेटा परत अलग किया है, केवल एक ही प्रकार प्रत्येक के साथ काम:

मैं की तरह कुछ करने के लिए इस refactor चाहिए। मैं प्रोफाइलर को आग लगाता हूं और ... ओह, एन + 1 चुनें। क्योंकि प्रत्येक क्षेत्र स्थान सेवा कहता है। हमारे पास केवल एक दर्जन क्षेत्र और 40 या तो स्थान हैं, इसलिए प्राकृतिक अनुकूलन DataLoadOptions का उपयोग करना है। समस्या यह है कि RegionRepository को पता नहीं है कि स्थानRepository एक ही डेटा कॉन्टेक्स्ट का उपयोग कर रहा है या नहीं। हम यहां कारखानों को इंजेक्शन दे रहे हैं, इसलिए स्थान रिपोजिटरी अपने आप को फैला सकता है। और यहां तक ​​कि अगर ऐसा नहीं होता है - मैं एक सेवा विधि को कॉल कर रहा हूं जो व्यवसाय वस्तुओं को प्रदान करता है, इसलिए डेटालोडऑप्शन का उपयोग किसी भी तरह से नहीं किया जा सकता है।

आह, मैंने कुछ अनदेखा किया। तो अब मैं

  return new Region { 
      InternalId = dbRegion.ID, 
      Name = dbRegion.Name, 
      // Now, LocationRepo needs to concern itself with Regions... 
      Locations = _locationRepo.Query() 
         .Select(loc => loc.RegionId == dbRegion.ID) 
         .ToList(), 
      // DefaultLocation is a uniqueidentifier 
      DefaultLocation = _locationRepo.Get(dbRegion.DefaultLocationId) 
     } 

कि अच्छा लग रहा है क्या करना होगा

public IQueryable<T> Query() 

: IRepository इस तरह की एक विधि है माना जाता है। सर्वप्रथम। दूसरे निरीक्षण पर, मेरे पास अलग-अलग व्यवसाय और एल 2 एस ऑब्जेक्ट्स हैं, इसलिए मुझे अभी भी यह नहीं दिख रहा है कि यह चयन एन + 1 से कैसे बचाता है क्योंकि क्वेरी GetTable<DbLocation> वापस नहीं आ सकती है।

समस्या में वस्तुओं के दो अलग-अलग सेट होने लगते हैं। लेकिन अगर मैं सभी ऑब्जेक्ट्स के साथ बिजनेस ऑब्जेक्ट्स को सजाने के लिए। डेटा। LINQ विशेषताएँ ([टेबल], [कॉलम] इत्यादि), जो अमूर्तता को तोड़ती है और आईरिपोजिटरी के उद्देश्य को हरा देती है। क्योंकि शायद मैं कुछ अन्य ओआरएम का उपयोग करने में भी सक्षम होना चाहता हूं, इस बिंदु पर मुझे अब अन्य विशेषताओं के साथ अपनी व्यावसायिक संस्थाओं को सजाना होगा (यदि व्यापारिक संस्थाएं अलग-अलग हैं। बिजनेस असेंबली, इसके उपभोक्ताओं को अब इसकी आवश्यकता है सभी ओआरएम को संदर्भित करने के लिए विशेषताओं के संदर्भ में - यक!)।

मेरे लिए, ऐसा लगता है कि IRepository IService होना चाहिए, और इसके बाद के संस्करण वर्ग इस तरह दिखना चाहिए:

public class RegionService : IRegionService { 
     private Func<DataContext> _l2sfactory; 

     public void Create(Region newRegion) { 
     // Responsibility 1: Business Validation 
     // This could of course move into the Region class as 
     // a bool IsValid(), but that doesn't change the fact that 
     // the service concerns itself with validation 
     if(newRegion.Locations == null || newRegion.Locations.Count == 0){ 
      throw new Exception("..."); 
     } 

     if(newRegion.DefaultLocation == null){ 
      newRegion.DefaultLocation = newRegion.Locations.First(); 
     } 

     // Responsibility 2: Data Insertion, incl. Foreign Keys 
     using(var db = _l2sfactory()){ 
      var dbRegion = new DbRegion { 
       ... 
      } 

      // Use EntitySet to insert Locations as well 
      foreach(var location in newRegion.Locations){ 
       var dbLocation = new DbLocation { 

       } 
       dbRegion.Locations.Add(dbLocation); 
      } 

      // Insert Region AND all Locations 
      db.InsertOnSubmit(dbRegion); 
      db.SubmitChanges(); 
     } 
     } 
} 

यह भी एक चिकन अंडे समस्या का हल:

  • DbRegion.ID है (के रूप में newid()) और IsDbGenerated = true सेट कर दिया जाता
  • DbRegion.DefaultLocationId गैर-व्यर्थ GUID
  • DbRegion.DefaultLocationId है डेटाबेस द्वारा उत्पन्न एक एफ है Location.ID
  • DbLocation.RegionId में कश्मीर है काफी असंभव तो जब तक आप डेटाबेस और इस कदम पर डेटा अखंडता का बलिदान गैर-व्यर्थ GUID और Region.ID

EntitySet बिना ऐसा करने में एक FK है, यह व्यापार तर्क में है, क्षेत्र प्रदाता से बाहर स्थानों के बारे में जिम्मेदारी रखना असंभव है।

मैं देख रहा हूँ कि यह कैसे पोस्टिंग, नहीं एक असली सवाल के रूप में देखा जा सकता है व्यक्तिपरक और तार्किक है, तो मुझे एक उद्देश्य सवाल तैयार करने के लिए समय दें:

  • वास्तव में भंडार पैटर्न दूर सार माना जाता है?
  • असली दुनिया में, लोगों को अमूर्तता को तोड़ने के बिना अपनी डेटाबेस परत को अनुकूलित कैसे किया जाता है, रिपोजिटरी पैटर्न को प्राप्त करना चाहिए?
  • विशेष रूप से, असली दुनिया SELECT N + 1 और डेटा अखंडता चिंताओं से कैसे निपटती है?

मुझे लगता है कि मेरी असली सवाल यह है:

  • जब पहले से ही एक ORM (SQL के लिए LINQ की तरह), DataContext पहले से ही अपने भंडार नहीं, और इस तरह DataContext के शीर्ष पर एक भंडार है का उपयोग कर बस है फिर से एक ही चीज़ का सारण करना?

उत्तर

1

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

4

अपने भंडारों को डिज़ाइन करते समय आपको समग्र रूट के रूप में जाना जाने वाला विचार करना चाहिए। अनिवार्य रूप से इसका अर्थ यह है कि यदि एक इकाई डोमेन के भीतर अकेले मौजूद हो सकती है तो यह अपने स्वयं के भंडार की अपेक्षा अधिक होगी। आपके मामले में यह क्षेत्र होगा।

क्लासिक ग्राहक/ऑर्डर परिदृश्य पर विचार करें।ग्राहक भंडार ऑर्डर तक पहुंच प्रदान करेगा क्योंकि एक ग्राहक के बिना ऑर्डर मौजूद नहीं हो सकता है और इसलिए जब तक आपके पास वैध व्यापारिक मामला नहीं है, तो आपको अलग ऑर्डर रिपोजिटरी की आवश्यकता नहीं है।

एक साधारण एप्लिकेशन में आपकी धारणा सही हो सकती है लेकिन याद रखें कि जब तक आप अपने एल 2 एस संदर्भ का एक अमूर्तता प्रदान नहीं करते हैं तो आप प्रभावी इकाई परीक्षण करने के लिए संघर्ष करेंगे। एक इंटरफ़ेस के खिलाफ कोडिंग, चाहे वह एक IServiceX हो, IRepositoryX या जो भी आपको अलगाव का स्तर प्रदान करता हो।

सेवा इंटरफेस डिजाइन में आने के बारे में निर्णय आम तौर पर व्यापार तर्क की जटिलता और उस तर्क में एक एक्स्टेंसिबल एपी की आवश्यकता से संबंधित है जो शायद कई अलग-अलग ग्राहकों द्वारा खाया जाता है।

+1

समस्या जो मैं देखता हूं वह है उदाहरण उदाहरण के साथ एक लहर प्रभाव है: एक आदेश में ऑर्डर इटम्स हैं, जो ऑर्डर के बिना मौजूद नहीं हो सकते हैं। हालांकि, मेरे पास इन्वेंटरी ट्रैकिंग के लिए एक फ़ंक्शन हो सकता है जो ट्रैक करता है कि कितनी इकाइयां बेची गईं। यह आइटम आईडी एसक्यूएल कॉल द्वारा ऑर्डरइटम समूह से एक सरल 'एसयूएम (मात्रा) है, लेकिन अब सभी ऑर्डर प्राप्त करने के लिए सभी ऑर्डर प्राप्त करने के लिए मेरी सूची ग्राहक रिपोजिटरी में जाना है। जब तक ग्राहकRepoistory एक 'IDictionary <आइटम, दशमलव> GetItemSalesQuantities' प्रदान करता है जो ग्राहक रिपोजिटरी पर जगह से बाहर निकलता है। –

+1

मैं इस उदाहरण में एक इन्वेंटरी रिपोजिटरी देखने के लिए अनुचित नहीं मानूंगा। ग्राहक और सूची के बीच कोई सीधा संबंध नहीं है और इसलिए दोनों में सेट आधारित ऑपरेशन के लिए कोई आवश्यकता नहीं है, जो दर्द का कारण बनता है और +1 का चयन करता है। –

+0

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

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

  • कोई संबंधित समस्या नहीं^_^