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 के शीर्ष पर एक भंडार है का उपयोग कर बस है फिर से एक ही चीज़ का सारण करना?
समस्या जो मैं देखता हूं वह है उदाहरण उदाहरण के साथ एक लहर प्रभाव है: एक आदेश में ऑर्डर इटम्स हैं, जो ऑर्डर के बिना मौजूद नहीं हो सकते हैं। हालांकि, मेरे पास इन्वेंटरी ट्रैकिंग के लिए एक फ़ंक्शन हो सकता है जो ट्रैक करता है कि कितनी इकाइयां बेची गईं। यह आइटम आईडी एसक्यूएल कॉल द्वारा ऑर्डरइटम समूह से एक सरल 'एसयूएम (मात्रा) है, लेकिन अब सभी ऑर्डर प्राप्त करने के लिए सभी ऑर्डर प्राप्त करने के लिए मेरी सूची ग्राहक रिपोजिटरी में जाना है। जब तक ग्राहकRepoistory एक 'IDictionary <आइटम, दशमलव> GetItemSalesQuantities' प्रदान करता है जो ग्राहक रिपोजिटरी पर जगह से बाहर निकलता है। –
मैं इस उदाहरण में एक इन्वेंटरी रिपोजिटरी देखने के लिए अनुचित नहीं मानूंगा। ग्राहक और सूची के बीच कोई सीधा संबंध नहीं है और इसलिए दोनों में सेट आधारित ऑपरेशन के लिए कोई आवश्यकता नहीं है, जो दर्द का कारण बनता है और +1 का चयन करता है। –
और अब हमें एक नया बिजनेस नियम मिल रहा है: मुझे गिनती चाहिए, लेकिन केवल दक्षिण अमेरिका में वीआईपी ग्राहकों के लिए, और केवल पिछले तीन महीनों में ऑर्डर के लिए। (वास्तव में ऐसा कुछ हुआ :()। यह अभी भी इन्वेंटरी रिपोजिटरी का काम है, लेकिन अब इसे एक से अधिक डेटाबेस टेबल तक पहुंचने की आवश्यकता है। उस समय मैं तर्क दूंगा कि यह अब एक साधारण भंडार नहीं है बल्कि एक बिजनेस सर्विस क्लास है? –