क्या रिपॉजिटरी को IQueryable लौटना चाहिए?


66

मैं ऐसे बहुत से प्रोजेक्ट देख रहा हूं जिनमें रिपॉजिटरी हैं जो कि इंस्टेंस को लौटाते हैं IQueryable। इससे अतिरिक्त फ़िल्टर और छंटाई IQueryableअन्य कोड द्वारा की जा सकती है , जो विभिन्न SQL उत्पन्न होने पर अनुवाद करता है। मैं उत्सुक हूं कि यह पैटर्न कहां से आया और क्या यह एक अच्छा विचार है।

मेरी सबसे बड़ी चिंता यह है कि IQueryableयह डेटाबेस में कुछ समय बाद हिट करने का एक वादा है, जब इसे एनुमरेट किया जाता है। इसका मतलब यह है कि रिपॉजिटरी के बाहर एक त्रुटि डाली जाएगी। इसका मतलब यह हो सकता है कि एक एंटिटी फ्रेमवर्क अपवाद को एप्लिकेशन की एक अलग परत में फेंक दिया गया है।

मैंने अतीत में कई एक्टिव रिजल्ट सेट्स (MARS) के साथ मुद्दों में भी भाग लिया है (विशेषकर लेनदेन का उपयोग करते समय) और यह दृष्टिकोण ऐसा लगता है कि यह अधिक बार ऐसा होगा।

डेटाबेस को रिपॉजिटरी कोड छोड़ने से पहले यह सुनिश्चित करने के लिए मैंने हमेशा अपने प्रत्येक LINQ एक्सप्रेशन के अंत में AsEnumerableया कॉल ToArrayकिया है।

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


यदि डिफरेंशियल क्वेरी निष्पादन बनाम क्वेरी निर्माण के समय डीबी उपलब्ध नहीं था, तो क्या अंतर होगा? उपभोग कोड की परवाह किए बिना उस स्थिति से निपटना होगा।
स्टीवन एवर्स

दिलचस्प सवाल है, लेकिन इसका जवाब देना मुश्किल होगा। एक साधारण खोज आपके प्रश्न के बारे में काफी गर्म बहस दिखाती है: duckduckgo.com/?q=repository+return+iqueryable
Corbin March

6
यह सब नीचे आता है कि क्या आप अपने ग्राहकों को लिंक् क्लॉज जोड़ने की अनुमति देना चाहते हैं जो आस्थगित निष्पादन से लाभान्वित हो सकते हैं। यदि वे whereएक स्थगित करने के लिए एक खंड जोड़ते हैं IQueryable, तो आपको केवल उस डेटा को तार पर भेजना होगा , न कि पूरे परिणाम सेट को।
रॉबर्ट हार्वे

यदि आप रिपॉजिटरी पैटर्न का उपयोग कर रहे हैं - नहीं, तो आपको IQueryable नहीं लौटाना चाहिए। यहाँ एक अच्छा क्यों है
अर्निस लैप्सा

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

जवाबों:


47

IQueryable को वापस करने से निश्चित रूप से रिपॉजिटरी के उपभोक्ताओं को अधिक लचीलापन मिलेगा। यह परिणाम को कम करने की जिम्मेदारी ग्राहक को देता है, जो स्वाभाविक रूप से लाभ और बैसाखी दोनों हो सकता है।

अच्छी तरफ, आपको रिपॉजिटरी मेथड्स (कम से कम इस लेयर पर) - GetAllActiveItems, GetAllNonActiveItems, इत्यादि बनाने की आवश्यकता नहीं होगी - जो डेटा आप चाहते हैं। आपकी पसंद के आधार पर, फिर से, यह अच्छा या बुरा हो सकता है। आपको व्यवहार अनुबंधों को परिभाषित करने की आवश्यकता होगी (जो आपके कार्यान्वयन का पालन करते हैं, लेकिन यह आपके लिए कहां तक ​​है)।

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

दूसरी ओर, अब आपने अपने उपयोगकर्ताओं को एक बन्दूक दी है। वे ऐसी चीजें कर सकते हैं जिनका आप इरादा नहीं कर सकते हैं (अति प्रयोग करना .include), भारी भारी प्रश्नों को करना और अपने संबंधित कार्यान्वयन आदि में इन-मेमोरी फ़िल्टरिंग करना, जो मूल रूप से लेयरिंग और व्यवहार नियंत्रणों का पक्ष लेंगे क्योंकि आपने दिए हैं पूर्ण पहुँच।

तो टीम, उनके अनुभव, ऐप के आकार, समग्र स्तर और वास्तुकला के आधार पर… यह निर्भर करता है: - \


ध्यान दें कि यदि आप इसके लिए काम करना चाहते थे, तो अपने IQueryable को लौटाएं, जो बदले में DB को कॉल करता है, लेकिन लेयरिंग और व्यवहार नियंत्रण जोड़ता है।
Psr

1
@psr क्या आप बता सकते हैं कि इससे आपका क्या मतलब है?
ट्रैविस पार्क

1
@TravisParks - IQueryable को DB द्वारा लागू नहीं किया जाना है, आप IQueryable वर्ग को स्वयं लिख सकते हैं - यह सिर्फ बहुत कठिन है। तो आप एक IQueryable के चारों ओर एक IQueryable आवरण लिख सकते हैं, और अंतर्निहित I DB तक आपकी IQueryable सीमा का पूर्ण उपयोग कर सकते हैं। लेकिन यह काफी कठिन है कि मैं इसे व्यापक रूप से करने की उम्मीद नहीं करूंगा।
Psr

2
ध्यान दें कि यहां तक ​​कि जब आप IQueryables नहीं लौटाते हैं, तो आप अभी भी कई तरीकों (GetAllActiveItems, GetAllNonActiveItems, आदि) को बस Expression<Func<Foo,bool>>अपने तरीके से पास करके रोक सकते हैं । इस पैरामीटर को Whereसीधे विधि में पारित किया जा सकता है , इस प्रकार उपभोक्ता को IQueryable <Foo> तक सीधी पहुंच की आवश्यकता के बिना अपने फ़िल्टर को चुनने में सक्षम बनाता है।
फ्लेटर

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

29

सार्वजनिक इंटरफेस के लिए IQueryable को उजागर करना एक अच्छा अभ्यास नहीं है। कारण मौलिक है: आप IQueryable बोध प्रदान नहीं कर सकते क्योंकि यह कहा जाता है।

सार्वजनिक इंटरफ़ेस प्रदाता और ग्राहकों के बीच एक अनुबंध है। और ज्यादातर मामलों में पूर्ण कार्यान्वयन की उम्मीद है। IQueryable एक धोखा है:

  1. IQueryable को लागू करना लगभग असंभव है। और वर्तमान में कोई उचित समाधान नहीं है। यहां तक ​​कि एंटिटी फ्रेमवर्क में बहुत सारे UnsupportedException हैं
  2. आज Queryable प्रदाताओं के पास बुनियादी ढांचे पर बहुत अधिक निर्भरता है। Sharepoint का अपना आंशिक कार्यान्वयन है और My SQL प्रदाता का आंशिक आंशिक कार्यान्वयन है।

रिपॉजिटरी पैटर्न हमें लेयरिंग द्वारा स्पष्ट प्रतिनिधित्व देता है, और, सबसे महत्वपूर्ण, प्राप्ति की स्वतंत्रता। इसलिए हम भविष्य के डेटा स्रोत में बदल सकते हैं।

सवाल यह है कि " क्या डेटा स्रोत प्रतिस्थापन की संभावना होनी चाहिए? "।

यदि डेटा का कल का हिस्सा SAP सेवाओं, NoSQL डेटा बेस, या केवल पाठ फ़ाइलों में स्थानांतरित किया जा सकता है, तो क्या हम IQueryable इंटरफ़ेस के उचित कार्यान्वयन की गारंटी दे सकते हैं?

( इस बुरे अभ्यास के बारे में अधिक अच्छे बिंदु)


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

1
धन्यवाद @LarsViklund, उदाहरण और समस्या वर्णन के साथ उत्तर अपडेट किया गया है
Artru

19

वास्तविक रूप से, यदि आपको आस्थगित निष्पादन चाहिए, तो आपको तीन विकल्प मिलेंगे:

  • इसे इस तरह से करें - एक को बेनकाब करें IQueryable
  • एक संरचना को लागू करें जो विशिष्ट फिल्टर या "प्रश्नों" के लिए विशिष्ट तरीकों को उजागर करती है। ( GetCustomersInAlaskaWithChildrenनीचे,)
  • एक संरचना को लागू करें जो दृढ़ता से टाइप किए गए फ़िल्टर / सॉर्ट / पेज एपीआई को उजागर करता है और IQueryableआंतरिक रूप से बनाता है ।

मैं तीसरा पसंद करता हूं (हालांकि मैंने सहकर्मियों को दूसरे को लागू करने में मदद की है), लेकिन इसमें स्पष्ट रूप से कुछ सेटअप और रखरखाव शामिल है। (बचाव के लिए T4!)

संपादित करें : मैं जिस बारे में बात कर रहा हूं, उसके आसपास के भ्रम को दूर करने के लिए, IQueryable कैन योर डॉग योर डॉग, स्टील योर वाइफ, किल योर विल टू लिव, आदि से चुराए गए उदाहरण पर विचार करें

परिदृश्य में जहां आप इस तरह से कुछ उजागर करेंगे:

public class CustomerRepo : IRepo 
{ 
     private DataContext ct; 
     public Customer GetCustomerById(int id) { ... } 
     public Customer[] GetCustomersInAlaskaWithChildren() { ... } 
} 

जिस API के बारे में मैं बात कर रहा हूं, वह आपको एक ऐसी विधि का खुलासा करने देगा जो आपको GetCustomersInAlaskaWithChildrenलचीले तरीके से (या मापदंड के किसी अन्य संयोजन) को व्यक्त करने देगा, और रेपो इसे IQuery के रूप में निष्पादित करेगा और आपको परिणाम लौटाएगा। लेकिन महत्वपूर्ण बात यह है कि यह रिपॉजिटरी परत के अंदर चलता है, इसलिए यह अभी भी स्थगित निष्पादन का लाभ उठाता है। एक बार जब आपको परिणाम वापस मिल जाते हैं, तब भी आप उस पर अपने दिल की सामग्री को LINQ-to-Objects कर सकते हैं।

इस तरह के दृष्टिकोण का एक और लाभ यह है कि, क्योंकि वे पोक्सो वर्ग के हैं, यह भंडार वेब या डब्ल्यूसीएफ सेवा के पीछे रह सकते हैं; यह AJAX या अन्य कॉलर्स के संपर्क में हो सकता है जो LINQ के बारे में पहली बात नहीं जानते हैं।


4
तो आप .NET एपीआई में निर्मित के लिए विकल्प के लिए एक क्वेरी एपीआई का निर्माण करेंगे?
माइकल ब्राउन

4
सुंदर मेरे लिए व्यर्थ लगता है
ozz

7
और मेरा उत्तर - - इस सवाल की बात @MikeBrown है कि आप को बेनकाब करना चाहते हैं व्यवहार या फायदे के IQueryable प्रकट किए बिना IQueryableही । आप अंतर्निहित API के साथ, ऐसा कैसे करने जा रहे हैं?
गांगेय कॉउबॉय

2
यह एक बहुत अच्छी तनातनी है। आपने यह नहीं बताया कि आप क्यों बचना चाहते हैं। हेक WCF डेटा सेवाएँ तार में IQueryable को उजागर करती हैं। मैं आप पर लेने की कोशिश नहीं कर रहा हूँ, मैं सिर्फ इस विरोध को समझ नहीं पा रहा हूँ जो कि IQueryable लोगों को लगता है।
माइकल ब्राउन

2
यदि आप केवल एक IQueryable का उपयोग करने जा रहे हैं, तो आप एक रिपॉजिटरी का उपयोग क्यों करेंगे? कार्यान्वयन विवरणों को छिपाने के लिए रिपॉजिटरी का उद्देश्य है। (इसके अलावा, WCF डेटा सेवाएँ पिछले 4 वर्षों में काम किए गए अधिकांश समाधानों की तुलना में थोड़ा नया है, जो इस तरह के एक भंडार का उपयोग करते हैं ... इसलिए यह संभावना नहीं है कि वे सिर्फ बनाने के लिए कभी भी अपडेट किए जाएंगे एक चमकदार नए खिलौने का उपयोग।)
गांगेय कॉव्बॉय

8

वास्तव में केवल एक वैध उत्तर है: यह इस बात पर निर्भर करता है कि रिपॉजिटरी का उपयोग कैसे किया जाए।

एक चरम पर आपकी रिपॉजिटरी एक DBContext के चारों ओर एक बहुत पतली आवरण है ताकि आप डेटाबेस-संचालित ऐप के आसपास परीक्षण क्षमता के लिबास को इंजेक्ट कर सकें। वास्तव में कोई वास्तविक दुनिया की उम्मीद नहीं है कि इसका उपयोग इसके बिना LINQ के अनुकूल DB के बिना डिस्कनेक्ट तरीके से किया जा सकता है क्योंकि आपके CRUD ऐप को कभी इसकी आवश्यकता नहीं है। तो फिर, क्यों, IQueryable का उपयोग नहीं करते? मैं संभवतः IEnumarable को पसंद करूंगा क्योंकि आपको अधिकांश लाभ मिलते हैं [पढ़ें: विलंबित निष्पादन] और यह उतना गंदा नहीं लगता है।

जब तक आप उस चरम पर नहीं बैठते हैं, तब तक मैं रिपॉजिटरी पैटर्न की भावना का लाभ उठाने की कोशिश करूंगा और उचित भौतिक संग्रह वापस करूंगा जिसमें अंतर्निहित डेटाबेस कनेक्शन का निहितार्थ नहीं है।


6
लौटने के संबंध में IEnumerable, यह नोट करना महत्वपूर्ण है कि (अनुमान एक है ) ((IEnumerable<T>)query).LastOrDefault()से बहुत अलग है । तो, यह केवल तब वापस लौटने के लिए समझ में आता है यदि आप वैसे भी सभी तत्वों के माध्यम से पुनरावृति करने जा रहे हैं। query.LastOrDefault()queryIQueryableIEnumerable
लू

7
 > Should Repositories return IQueryable?

नहीं, यदि आप टेस्टड्राइव डेवलपमेंट / अनइटिंग करना चाहते हैं, क्योंकि डेटाबेस या अन्य IQueryable प्रदाता से अलगाव में अपने व्यावसायिक (= रिपॉजिटरी-उपभोक्ता) परीक्षण के लिए नकली रिपॉजिटरी बनाने का कोई आसान तरीका नहीं है।


6

एक शुद्ध वास्तुकला के दृष्टिकोण से, IQueryable एक टपका हुआ अमूर्त है। एक सामान्यता के रूप में, यह उपयोगकर्ता को बहुत अधिक शक्ति प्रदान करता है। यह कहा जा रहा है, ऐसी जगहें हैं जहां यह समझ में आता है। यदि आप OData का उपयोग कर रहे हैं, तो IQueryable एक समापन बिंदु प्रदान करना बेहद आसान बनाता है जो आसानी से फ़िल्टर करने योग्य, छांटे जाने योग्य, समूहनीय ... आदि है। मैं वास्तव में डीटीओ बनाना पसंद करता हूं जो कि मेरी इकाइयाँ मानचित्र पर बनाती हैं और IQueryable इसके बजाय DTO लौटाता है। इस तरह से मैं अपने डेटा को प्री-फिल्टर करता हूं और वास्तव में केवल परिणामों के क्लाइंट अनुकूलन की अनुमति देने के लिए IQueryable विधि का उपयोग करता हूं।


6

मैंने इस तरह की पसंद का सामना किया है।

तो, आइए सकारात्मक और नकारात्मक पक्षों को संक्षेप में प्रस्तुत करें:

सकारात्मक:

  • लचीलापन। यह निश्चित रूप से लचीला और सुविधाजनक है कि ग्राहक पक्ष को केवल एक रिपॉजिटरी विधि के साथ कस्टम क्वेरी बनाने की अनुमति दी जाए

नकारात्मक:

  • अप्राप्य । (आप वास्तव में अंतर्निहित IQueryable कार्यान्वयन का परीक्षण करेंगे, जो आमतौर पर आपका ORM है। लेकिन आपको इसके बजाय अपने तर्क का परीक्षण करना चाहिए)
  • जिम्मेदारी blurs । यह कहना असंभव है, कि आपके भंडार का वह विशेष तरीका क्या है। दरअसल, यह एक ईश्वर-विधि है, जो आपके संपूर्ण डेटाबेस में आपकी पसंद की कोई भी चीज़ लौटा सकती है
  • असुरक्षित । इस रिपॉजिटरी विधि से क्या किया जा सकता है, इस पर कोई प्रतिबंध नहीं होने के साथ, कुछ मामलों में ऐसे कार्यान्वयन सुरक्षा बिंदु से असुरक्षित हो सकते हैं।
  • कैशिंग समस्याओं (या, सामान्य होने के लिए, किसी भी पूर्व या बाद के प्रसंस्करण की समस्याओं)। यह आम तौर पर नासमझ है, उदाहरण के लिए, आपके आवेदन में सब कुछ कैश। आप कुछ को कैश करने की कोशिश करेंगे, जो आपके डेटाबेस को महत्वपूर्ण भार देगा। लेकिन ऐसा कैसे करें, यदि आपके पास केवल एक रिपॉजिटरी विधि है, जो क्लाइंट डेटाबेस के खिलाफ लगभग किसी भी क्वेरी को जारी करने के लिए उपयोग करते हैं?
  • लॉगिंग समस्याएं । रिपॉजिटरी लेयर पर कुछ लॉग करने से आप यह जांचने के लिए IQueryable का निरीक्षण करेंगे कि इस विशेष कॉल को लॉग इन किया जाना चाहिए या नहीं।

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


5

मुझे लगता है कि विकास के प्रारंभिक चरणों के दौरान आपकी रिपॉजिटरी पर IQuery का चयन पूरी तरह से स्वीकार्य है। यूआई उपकरण हैं जो IQueryable के साथ सीधे काम कर सकते हैं और छँटाई, फ़िल्टरिंग, ग्रुपिंग आदि को संभाल सकते हैं।

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

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


4

मैंने जो देखा है (और जो मैं खुद को लागू कर रहा हूं) निम्नलिखित है:

public interface IEntity<TKey>
{
    TKey Id { get; set; }
}

public interface IRepository<TEntity, in TKey> where TEntity : IEntity<TKey>
{
    void Create(TEntity entity);
    TEntity Get(TKey key);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> expression);
    void Update(TEntity entity);
    void Delete(TKey id);
}

यह आपको क्या करने की अनुमति देता है किसी भी LINQy व्यवसाय को उजागर किए बिना IQueryable.Where(a => a.SomeFilter)अपने रेपो पर एक क्वेरी करने की लचीलापन है concreteRepo.GetAll(a => a.SomeFilter)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.