IEnumerable लौटना <T> बनाम IQueryable <T>


1084

रिटर्निंग IQueryable<T>बनाम के बीच अंतर क्या है IEnumerable<T>, जब एक को दूसरे पर प्राथमिकता दी जानी चाहिए?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

जवाबों:


1778

हाँ, दोनों आपको आस्थगित निष्पादन देंगे ।

अंतर यह है कि IQueryable<T>इंटरफ़ेस है जो LINQ-to-SQL (LINQ.-to-something वास्तव में) को काम करने की अनुमति देता है। इसलिए यदि आप अपनी क्वेरी को एक पर फिर से परिष्कृत करते हैं IQueryable<T>, तो संभव है कि डेटाबेस में उस क्वेरी को निष्पादित किया जाएगा।

के लिए IEnumerable<T>मामला है, यह LINQ करने वाली वस्तु हो जाएगा, जिसका अर्थ है कि मूल क्वेरी से मेल सभी वस्तुओं डेटाबेस से मेमोरी में लोड करना होगा।

कोड में:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

यह कोड SQL को केवल सोने के ग्राहकों का चयन करेगा। निम्नलिखित कोड, दूसरी ओर, डेटाबेस में मूल क्वेरी को निष्पादित करेगा, फिर मेमोरी में गैर-स्वर्ण ग्राहकों को फ़िल्टर करना होगा:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

यह काफी महत्वपूर्ण अंतर है, और IQueryable<T>कई मामलों में आप डेटाबेस से कई पंक्तियों को वापस लौटने से बचा सकते हैं। एक और प्रमुख उदाहरण पेजिंग कर रहा है: यदि आप उपयोग करते हैं Takeऔर Skipचालू करते हैं IQueryable, तो आपको केवल अनुरोधित पंक्तियों की संख्या मिल जाएगी; ऐसा IEnumerable<T>करने से आपकी सभी पंक्तियाँ स्मृति में लोड हो जाएंगी।


32
महान व्याख्या। क्या ऐसी कोई स्थिति है जहां IEnumerable IQueryable के लिए बेहतर होगा?
fjxx

8
तो हम कह सकते हैं कि यदि हम मेमोरी ऑब्जेक्ट को क्वेरी करने के लिए IQueryable का उपयोग कर रहे हैं, तो वे IEnumerable और IQueryable के बीच कोई अंतर नहीं होगा?
तारिक

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

48
@fjxx हाँ। यदि आप अपने मूल परिणाम (कई अंतिम परिणाम) पर बार-बार फ़िल्टरिंग चाहते हैं। ऐसा करने पर कि IQueryable इंटरफ़ेस डेटाबेस के लिए कई
चक्कर लगाएगा

34
पसंद IEnumerableकरने IQueryableका एक और कारण यह है कि सभी LINQ ऑपरेशन सभी LINQ प्रदाताओं द्वारा समर्थित नहीं हैं। इसलिए जब तक आप जानते हैं कि आप क्या कर रहे हैं, आप IQueryableLINQ प्रदाता (LINQ2SQL, EF, NHibernate, MongoDB आदि) को क्वेरी के रूप में ज्यादा से ज्यादा पुश करने के लिए उपयोग कर सकते हैं । लेकिन अगर आप अन्य कोड को अपने साथ जो कुछ भी करना चाहते हैं उसे करने दें, IQueryableतो अंतत: आप मुसीबत में पड़ जाएंगे क्योंकि कुछ क्लाइंट कोड ने कहीं एक असमर्थित ऑपरेशन का उपयोग किया है। मैं IQueryableरिपॉजिटरी या समतुल्य परत के अतीत को "जंगली में" जारी न करने की सिफारिश से सहमत हूं ।
अवि

302

शीर्ष उत्तर अच्छा है, लेकिन इसमें अभिव्यक्ति के पेड़ों का उल्लेख नहीं है जो बताते हैं कि "दो इंटरफेस" कैसे अलग हैं। असल में, LINQ एक्सटेंशन के दो समान सेट हैं। Where(), Sum(), Count(), FirstOrDefault()एक है कि कार्यों स्वीकार करता है और एक है कि भाव स्वीकार करता है:, आदि सब दो संस्करण हैं।

  • IEnumerableसंस्करण हस्ताक्षर है:Where(Func<Customer, bool> predicate)

  • IQueryableसंस्करण हस्ताक्षर है:Where(Expression<Func<Customer, bool>> predicate)

आप शायद इसे साकार करने के बिना उन दोनों का उपयोग कर रहे हैं क्योंकि दोनों को समान वाक्यविन्यास का उपयोग करके कहा जाता है:

जैसे Where(x => x.City == "<City>")दोनों पर काम करता है IEnumerableऔरIQueryable

  • Where()एक IEnumerableसंग्रह पर उपयोग करते समय , संकलक एक संकलित फ़ंक्शन को पास करता हैWhere()

  • Where()एक IQueryableसंग्रह पर उपयोग करते समय , संकलक एक अभिव्यक्ति ट्री को पास करता है Where()। एक अभिव्यक्ति पेड़ प्रतिबिंब प्रणाली की तरह है लेकिन कोड के लिए है। संकलक आपके कोड को एक डेटा संरचना में परिवर्तित करता है जो बताता है कि आपका कोड एक प्रारूप में क्या करता है जो आसानी से पचने योग्य है।

क्यों इस अभिव्यक्ति पेड़ बात के साथ परेशान? मैं सिर्फ Where()अपना डेटा फ़िल्टर करना चाहता हूं। मुख्य कारण यह है कि EF और Linq2SQL ORM दोनों ही अभिव्यक्ति के पेड़ को सीधे SQL में बदल सकते हैं जहाँ आपका कोड बहुत तेजी से क्रियान्वित होगा।

ओह, यह एक मुफ्त प्रदर्शन को बढ़ावा देने जैसा लगता है, क्या मुझे AsQueryable()उस मामले में सभी जगह उपयोग करना चाहिए ? नहीं, IQueryableकेवल उपयोगी है यदि अंतर्निहित डेटा प्रदाता इसके साथ कुछ कर सकता है। किसी चीज Listको नियमित रूप से बदलने से IQueryableआपको कोई लाभ नहीं होगा।


9
IMO यह स्वीकृत उत्तर से बेहतर है। हालाँकि, मुझे एक चीज़ नहीं मिलती है: IQueryable नियमित वस्तुओं के लिए कोई लाभ नहीं देता है, ठीक है, लेकिन क्या यह किसी भी तरह से खराब है? क्योंकि अगर यह सिर्फ कोई लाभ नहीं देता है, तो यह IEnumerable को पसंद करने के लिए पर्याप्त कारण नहीं है, इसलिए सभी जगह पर IQueryable का उपयोग करने का विचार वैध है।
सर्गेई टैचेनोव

1
सर्गेई, IQueryable IEnumerable का विस्तार करता है इसलिए IQueryable का उपयोग करते समय आप एक IEnumerable इंस्टेंटेशन की तुलना में मेमोरी में अधिक लोड करेंगे! तो यहाँ एक तर्क है। ( stackoverflow.com/questions/12064828/… c ++ हालांकि मुझे लगा कि मैं इसे हटा सकता हूं)
वाइकिंग

सर्गेई के साथ इस बारे में सहमत होना सबसे अच्छा जवाब है (हालांकि स्वीकृत जवाब ठीक है)। मैं अपने अनुभव में यह जोड़ना चाहता हूं कि IQueryableकार्यों को पार्स नहीं IEnumerableकरता है: उदाहरण के लिए, यदि आप जानना चाहते हैं कि कौन से तत्व एक DBSet<BookEntity>में नहीं हैं List<BookObject>, तो dbSetObject.Where(e => !listObject.Any(o => o.bookEntitySource == e))एक अपवाद फेंकता है Expression of type 'BookEntity' cannot be used for parameter of type 'BookObject' of method 'Boolean Contains[BookObject] (IEnumerable[BookObject], BookObject)':। मुझे .ToList()बाद में जोड़ना था dbSetObject
जीन-डेविड लैंज

80

हां, दोनों आस्थगित निष्पादन का उपयोग करते हैं। एसक्यूएल सर्वर प्रोफाइलर का उपयोग करते हुए अंतर को स्पष्ट करते हैं ....

जब हम निम्नलिखित कोड चलाते हैं:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

SQL सर्वर प्रोफाइलर में हम निम्न के बराबर एक कमांड पाते हैं:

"SELECT * FROM [dbo].[WebLog]"

वेबलॉग टेबल के खिलाफ कोड के उस ब्लॉक को चलाने में लगभग 90 सेकंड लगते हैं, जिसमें 1 मिलियन रिकॉर्ड हैं।

इसलिए, सभी टेबल रिकॉर्ड्स को ऑब्जेक्ट्स के रूप में मेमोरी में लोड किया जाता है, और फिर प्रत्येक के साथ। जहां () यह इन ऑब्जेक्ट्स के खिलाफ मेमोरी में एक और फिल्टर होगा।

जब हम उपरोक्त उदाहरण (दूसरी पंक्ति) के IQueryableबजाय उपयोग करते हैं IEnumerable:

SQL सर्वर प्रोफाइलर में हम निम्न के बराबर एक कमांड पाते हैं:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

कोड के इस ब्लॉक को चलाने में लगभग चार सेकंड लगते हैं IQueryable

IQueryable के पास एक संपत्ति होती है जिसे Expressionट्री ट्री एक्सप्रेशन संग्रहीत किया जाता है जो resultहमारे उदाहरण में उपयोग किए जाने पर निर्मित होता है (जिसे आस्थगित निष्पादन कहा जाता है), और अंत में इस एक्सप्रेशन को डेटाबेस इंजन पर चलाने के लिए SQL क्वेरी में परिवर्तित किया जाएगा।


5
IEnumerable के लिए कास्टिंग करते समय यह मुझे सिखाता है, अंतर्निहित IQueryable यह IQueryable एक्सटेंशन विधि खो देता है।
यिंग

56

दोनों आपको आस्थगित निष्पादन देंगे, हाँ।

जैसा कि दूसरे के लिए पसंद किया जाता है, यह इस बात पर निर्भर करता है कि आपका अंतर्निहित डेटा स्रोत क्या है।

IEnumerableअपने संग्रह को क्वेरी करने के लिए LINQ का उपयोग करके स्वचालित रूप से रनटाइम के लिए वसीयत वापस करना बाध्य करेगा।

एक IQueryable(जो लागू होता है IEnumerable, वैसे) लौटकर आपकी क्वेरी को किसी ऐसी चीज़ में अनुवाद करने के लिए अतिरिक्त कार्यक्षमता प्रदान करता है जो अंतर्निहित स्रोत (LINQ to SQL, LINQ to XML, आदि) पर बेहतर प्रदर्शन कर सकती है।


30

सामान्य शब्दों में, मैं निम्नलिखित की सिफारिश करूंगा:

  • IQueryable<T>यदि आप निष्पादन से पहले वापस आने वाली क्वेरी को परिष्कृत करने के लिए अपने तरीके का उपयोग करके डेवलपर को सक्षम करना चाहते हैं तो वापस लौटें ।

  • IEnumerableयदि आप वस्तुओं का एक समूह परिवहन करना चाहते हैं तो वापस लौटें ।

एक कल्पना कीजिए IQueryableकि यह क्या है - डेटा के लिए "क्वेरी" (जिसे आप चाहें तो परिष्कृत कर सकते हैं)। एक IEnumerableऑब्जेक्ट का एक सेट है (जो पहले से ही प्राप्त किया गया था या बनाया गया था) जिस पर आप गणना कर सकते हैं।


2
"गणना कर सकते हैं," नहीं "IEnumerable कर सकते हैं।"
केसी

28

पहले बहुत कुछ कहा गया है, लेकिन जड़ों तक वापस, अधिक तकनीकी तरीके से:

  1. IEnumerable स्मृति में वस्तुओं का एक संग्रह है जिसे आप गणना कर सकते हैं - एक इन-मेमोरी अनुक्रम जो इसके माध्यम से पुनरावृति करना संभव बनाता है (यह foreachलूप के लिए आसान रास्ता बनाता है , हालांकि आप IEnumeratorकेवल साथ जा सकते हैं )। वे स्मृति में रहते हैं।
  2. IQueryable एक अभिव्यक्ति पेड़ है जो अंतिम परिणाम पर गणना करने की क्षमता के साथ कुछ बिंदु पर कुछ और में अनुवाद किया जाएगा । मुझे लगता है कि यह वही है जो ज्यादातर लोगों को भ्रमित करता है।

वे स्पष्ट रूप से विभिन्न अर्थ हैं।

IQueryableएक अभिव्यक्ति ट्री (एक क्वेरी, बस) का प्रतिनिधित्व करता है जिसे अंतर्निहित क्वेरी प्रदाता द्वारा कुछ और के लिए अनुवाद किया जाएगा जैसे ही रिलीज एपीआई को बुलाया जाता है, जैसे कि LINQ एग्रीगेट फ़ंक्शन (सम, काउंट, आदि) या टोलिस्ट [एरे, डिक्शनरी]। ..]। और IQueryableऑब्जेक्ट भी लागू होते हैं IEnumerable, IEnumerable<T>ताकि यदि वे किसी क्वेरी का प्रतिनिधित्व करते हैं , तो उस क्वेरी का परिणाम पुनरावृत्त हो सके। इसका मतलब है कि IQueryable को केवल पूछताछ करने की आवश्यकता नहीं है। सही शब्द वे अभिव्यक्ति पेड़ हैं

अब उन अभिव्यक्तियों को कैसे निष्पादित किया जाता है और वे जो मोड़ लेते हैं, वह तथाकथित क्वेरी प्रदाताओं (अभिव्यक्ति निष्पादकों को हम उनके बारे में सोच सकते हैं) तक है।

में इकाई की रूपरेखा दुनिया (जो कि रहस्यमय अंतर्निहित डेटा स्रोत प्रदाता, या क्वेरी प्रदाता है) IQueryableभाव देशी में अनुवाद किया T-SQL प्रश्नों। Nhibernateउनके साथ भी ऐसी ही बातें करता है। आप LINQ में वर्णित अवधारणाओं को अच्छी तरह से अनुसरण करते हुए अपना एक लिख सकते हैं : उदाहरण के लिए, एक IQueryable प्रदाता लिंक का निर्माण , और आप अपने उत्पाद स्टोर प्रदाता सेवा के लिए एक कस्टम क्वेरी API चाहते हो सकता है।

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

जैसे कि निष्पादन को स्थगित करने के लिए यह LINQस्मृति में अभिव्यक्ति ट्री योजना को रखने और इसे केवल मांग पर निष्पादन में भेजने की सुविधा है, जब भी कुछ एपीआई को अनुक्रम के खिलाफ कहा जाता है (एक ही गणना, टोलिस्ट, आदि)।

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

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


3
IEnumerables हमेशा स्मृति में है कि टिप्पणी nossassassately सच नहीं है। IQueryable इंटरफ़ेस IEnumerable इंटरफ़ेस को लागू करता है। इस वजह से, आप एक कच्चे IQueryable को पारित कर सकते हैं जो एक LINQ-to-SQL क्वेरी का प्रतिनिधित्व करता है जो एक IEnumerable की अपेक्षा करता है! आपको यह जानकर आश्चर्य हो सकता है कि आपके डेटा का संदर्भ समाप्त हो गया है या आप MARS (एकाधिक सक्रिय परिणाम सेट) के साथ समस्याओं में भाग रहे हैं।

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

@AlexanderPritchard उनमें से कोई भी स्मृति में ऑब्जेक्ट नहीं हैं क्योंकि वे वास्तविक प्रकार के प्रति नहीं हैं, वे एक प्रकार के मार्कर हैं - यदि आप उस गहरे में जाना चाहते हैं। लेकिन यह समझ में आता है (और यही कारण है कि MSDN ने इसे इस तरह से रखा है) IEnumerables को मेमोरी मेमोरी के रूप में सोचने के लिए जबकि IQueryables अभिव्यक्ति पेड़ों के रूप में। मुद्दा यह है कि IQueryable इंटरफ़ेस IEnumerable इंटरफ़ेस को इनहेरिट करता है ताकि यदि यह किसी क्वेरी का प्रतिनिधित्व करता है, तो उस क्वेरी के परिणामों को गणना की जा सके। एन्यूमरेशन एक IQueryable ऑब्जेक्ट के साथ जुड़े अभिव्यक्ति ट्री को निष्पादित करने का कारण बनता है।
बजे अरमान मैकहिटेरियन

24

सामान्य तौर पर आप क्वेरी के मूल स्थिर प्रकार को तब तक संरक्षित रखना चाहते हैं जब तक यह मायने नहीं रखता।

इस कारण से, आप अपने चर को IQueryable<>या तो के बजाय 'var' के रूप में परिभाषित कर सकते हैं IEnumerable<>और आपको पता चल जाएगा कि आप प्रकार नहीं बदल रहे हैं।

यदि आप एक से शुरू करते हैं IQueryable<>, तो आप आमतौर पर इसे IQueryable<>तब तक रखना चाहते हैं जब तक कि इसे बदलने के लिए कोई सम्मोहक कारण न हो। इसका कारण यह है कि आप क्वेरी प्रोसेसर को अधिक से अधिक जानकारी देना चाहते हैं। उदाहरण के लिए, यदि आप केवल 10 परिणामों (आपने कॉल किया है Take(10)) का उपयोग करने जा रहे हैं, तो आप चाहते हैं कि SQL सर्वर इसके बारे में जाने ताकि वह अपनी क्वेरी योजनाओं को ऑप्टिमाइज़ कर सके और आपको केवल वही डेटा भेज सके जो आप उपयोग करेंगे।

प्रकार बदलने के लिए एक बाध्यकारी कारण से IQueryable<>करने के लिए IEnumerable<>हो सकता है कि आप कुछ विस्तार समारोह है कि के कार्यान्वयन बुला रहे हैं IQueryable<>अपने विशेष वस्तु में या तो संभाल या हैंडल नहीं कर सकता अकुशलता से। उस स्थिति में, आप टाइप को IEnumerable<>( उदाहरण के लिए एक्सटेंशन विधि IEnumerable<>का उपयोग करके AsEnumerable) एक चर में परिवर्तित करने की इच्छा कर सकते हैं, ताकि आप जिस एक्सटेंशन फ़ंक्शंस को कॉल करते हैं, वह Enumerableक्लास के बजाय Queryableक्लास में हो।


18

IEnumerable<T>LINQ क्वेरी के प्रदर्शन को नाटकीय रूप से प्रभावित करने के तरीके के बारे में संक्षिप्त स्रोत कोड नमूने के साथ एक ब्लॉग पोस्ट है : एंटिटी फ्रेमवर्क: IQueryable बनाम IEnumerable

यदि हम गहराई से खुदाई करते हैं और स्रोतों पर गौर करते हैं, तो हम देख सकते हैं कि स्पष्ट रूप से अलग-अलग विस्तार विधियाँ हैं IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

और IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

पहला एक प्रगणक पुनरावृत्ति देता है, और दूसरा IQueryableस्रोत में निर्दिष्ट क्वेरी प्रदाता के माध्यम से क्वेरी बनाता है ।


11

मैं हाल ही में IEnumerablev। के साथ एक समस्या में भाग गया IQueryable। पहले इस्तेमाल किए जा रहे एल्गोरिदम ने IQueryableपरिणामों का एक सेट प्राप्त करने के लिए एक क्वेरी का प्रदर्शन किया । इन्हें तब एक foreachलूप में दिया गया था , जिसमें एंटिटी फ्रेमवर्क (EF) वर्ग के रूप में आइटम को तत्काल लाया गया था। इस EF वर्ग का उपयोग तब fromLinq से Entity क्वेरी के खंड में किया गया था , जिसके परिणामस्वरूप परिणाम हुआ IEnumerable

मैं EF और Linq Entities के लिए काफी नया हूं, इसलिए यह पता लगाने में थोड़ा समय लगा कि क्या अड़चन थी। मिनीप्रोफाइलिंग का उपयोग करते हुए, मैंने क्वेरी को पाया और फिर सभी अलग-अलग ऑपरेशनों को IQueryableएंटिटीज़ क्वेरी के लिए एकल लाइनक में परिवर्तित कर दिया । IEnumerable15 सेकंड लिया और IQueryableनिष्पादित करने के लिए 0.5 सेकंड लिया। इसमें तीन तालिकाओं को शामिल किया गया था और इसे पढ़ने के बाद, मेरा मानना ​​है कि IEnumerableक्वेरी वास्तव में तीन तालिका क्रॉस-उत्पाद बना रही थी और परिणामों को फ़िल्टर कर रही थी।

एक नियम के रूप में IQueryables का उपयोग करने की कोशिश करें और अपने परिवर्तनों को मापने योग्य बनाने के लिए अपने काम को प्रोफाइल करें।


कारण यह था कि IQueryable भाव EF में एक देशी SQL में परिवर्तित हो जाते हैं और DB में सही निष्पादित होते हैं जबकि IEnumerable सूचियाँ इन-मेमोरी ऑब्जेक्ट हैं। जब आप गणना, सम या किसी भी ... जैसे कुल कार्यों को बुलाते हैं और बाद में स्मृति में काम करते हैं, तो वे किसी बिंदु पर डीबी से प्राप्त होते हैं। IQueryables भी एक बार उन API में से किसी एक को कॉल करने के बाद मेमोरी में अटक जाते हैं लेकिन यदि नहीं, तो आप लेयर्स के स्टैक तक एक्सप्रेशन पास कर सकते हैं और API कॉल होने तक फ़िल्टर के साथ चारों ओर खेल सकते हैं। अच्छी तरह से डिज़ाइन की गई DAL एक अच्छी डिज़ाइन की गई रिपॉजिटरी इस तरह की समस्याओं को हल करेगी;)
अरमान मैकहिटेरियन

10

मैं प्रतीत होता है परस्पर विरोधी प्रतिक्रियाओं (ज्यादातर IEnumerable आसपास) के कारण कुछ चीजें स्पष्ट करना चाहूंगा।

(1) इंटरफ़ेस का IQueryableविस्तार करता IEnumerableहै। (आप किसी IQueryableऐसी चीज़ को भेज सकते हैं जो IEnumerableत्रुटि के बिना उम्मीद करती है ।)

(2) दोनों IQueryableऔर IEnumerableLINQ परिणाम सेट पर पुनरावृत्ति करते समय आलसी लोडिंग का प्रयास करते हैं। (ध्यान दें कि कार्यान्वयन प्रत्येक प्रकार के लिए इंटरफ़ेस एक्सटेंशन विधियों में देखा जा सकता है।)

दूसरे शब्दों में, IEnumerablesविशेष रूप से "इन-मेमोरी" नहीं हैं। IQueryablesडेटाबेस पर हमेशा निष्पादित नहीं होते हैं। IEnumerableचीजों को मेमोरी में लोड करना चाहिए (एक बार प्राप्त करने के बाद, संभवतः आलसी), क्योंकि इसमें कोई सार डेटा प्रदाता नहीं है। IQueryablesएक सार प्रदाता (जैसे LINQ-to-SQL) पर भरोसा करें, हालाँकि यह .NET इन-मेमोरी प्रदाता भी हो सकता है।

नमूना उपयोग के मामले

(ए) IQueryableईएफ संदर्भ से रिकॉर्ड की सूची प्राप्त करें । (कोई रिकॉर्ड स्मृति में नहीं हैं।)

(बी) IQueryableएक दृश्य जिसका मॉडल है, के पास IEnumerable। (मान्य है। IQueryableविस्तार IEnumerable)

(c) डेटा सेट के रिकॉर्ड्स, चाइल्ड एंटिटीज़ और प्रॉपर्टीज़ को देखने के लिए ओवररेट और एक्सेस करें। (अपवादों का कारण हो सकता है!)

संभव मुद्दे

(1) IEnumerableआलसी लोडिंग के प्रयास और आपका डेटा संदर्भ समाप्त हो गया है। अपवाद को फेंक दिया गया क्योंकि प्रदाता अब उपलब्ध नहीं है।

(2) एंटिटी फ्रेमवर्क इकाई प्रॉक्सी सक्षम होती हैं (डिफ़ॉल्ट), और आप संबंधित (आभासी) वस्तु की समय सीमा समाप्त संदर्भ के साथ उपयोग करने का प्रयास करते हैं। समान (१)।

(3) मल्टीपल एक्टिव रिजल्ट सेट (MARS)। यदि आप IEnumerableकिसी foreach( var record in resultSet )ब्लॉक में देख रहे हैं और एक साथ एक्सेस करने का प्रयास कर रहे हैं record.childEntity.childProperty, तो आप डेटा सेट और रिलेशनल यूनिट दोनों के आलसी लोडिंग के कारण MARS के साथ समाप्त हो सकते हैं। यह एक अपवाद का कारण होगा यदि यह आपके कनेक्शन स्ट्रिंग में सक्षम नहीं है।

समाधान

  • मैंने पाया है कि कनेक्शन स्ट्रिंग में MARS को सक्षम करना अविश्वसनीय रूप से काम करता है। मेरा सुझाव है कि आप MARS से बचें जब तक कि यह अच्छी तरह से समझा और स्पष्ट रूप से वांछित नहीं है।

क्वेरी और स्टोर परिणामों को लागू करके निष्पादित करें resultList = resultSet.ToList() यह लगता है कि आपकी इकाइयां स्मृति में हैं यह सुनिश्चित करने का सबसे सीधा तरीका है।

ऐसे मामलों में जहां आप संबंधित संस्थाओं तक पहुंच रहे हैं, आपको अभी भी डेटा संदर्भ की आवश्यकता हो सकती है। या तो आप या आप से इकाई अस्तित्व और स्पष्ट रूप Includeसे संबंधित संस्थाओं को निष्क्रिय कर सकते हैं DbSet


9

"IEnumerable" और "IQueryable" के बीच मुख्य अंतर उस बारे में है जहां फ़िल्टर तर्क निष्पादित किया जाता है। एक क्लाइंट की तरफ (मेमोरी में) और दूसरा डेटाबेस पर निष्पादित होता है।

उदाहरण के लिए, हम एक उदाहरण पर विचार कर सकते हैं, जहां हमारे डेटाबेस में एक उपयोगकर्ता के लिए हमारे पास 10,000 रिकॉर्ड हैं और चलो केवल 900 बाहर हैं जो सक्रिय उपयोगकर्ता हैं, इसलिए इस मामले में यदि हम "IEnumerable" का उपयोग करते हैं तो पहले यह मेमोरी में सभी 10,000 रिकॉर्ड लोड करता है और ... फिर उस पर IsActive फ़िल्टर लागू करता है जो अंततः 900 सक्रिय उपयोगकर्ताओं को वापस करता है।

जबकि दूसरी ओर उसी मामले पर अगर हम "IQueryable" का उपयोग करते हैं तो यह सीधे डेटाबेस पर IsActive फ़िल्टर को लागू करेगा जो सीधे वहां से 900 सक्रिय उपयोगकर्ताओं को लौटाएगा।

संदर्भ लिंक


प्रदर्शन के मामले में कौन सा अनुकूलित और हल्का वजन है?
सिटाकोर सैम

@Sam "IQueryable" को अनुकूलित और हल्के वजन के मामले में अधिक पसंद किया जाता है।
ताबिश उस्मान

6

हम एक ही तरह से दोनों का उपयोग कर सकते हैं, और वे केवल प्रदर्शन में अलग हैं।

IQueryable केवल एक कुशल तरीके से डेटाबेस के खिलाफ निष्पादित करता है। इसका मतलब है कि यह एक संपूर्ण चयन क्वेरी बनाता है और केवल संबंधित रिकॉर्ड प्राप्त करता है।

उदाहरण के लिए, हम उन शीर्ष 10 ग्राहकों को लेना चाहते हैं, जिनका नाम 'निमल' से शुरू होता है। इस स्थिति में चुनिंदा क्वेरी को जनरेट किया जाएगा select top 10 * from Customer where name like ‘Nimal%’

लेकिन अगर हम IEnumerable का उपयोग करते हैं, तो क्वेरी की तरह होगा select * from Customer where name like ‘Nimal%’और शीर्ष दस को C # कोडिंग स्तर पर फ़िल्टर किया जाएगा (यह डेटाबेस से सभी ग्राहक रिकॉर्ड प्राप्त करता है और उन्हें C # में पास करता है)।


5

पहले 2 के अलावा वास्तव में अच्छे उत्तर (ड्रिस और जैकब द्वारा):

IEnumerable इंटरफ़ेस System.Collections नामस्थान में है।

IEnumerable ऑब्जेक्ट मेमोरी में डेटा के एक सेट का प्रतिनिधित्व करता है और केवल इस डेटा को आगे बढ़ा सकता है। IEnumerable ऑब्जेक्ट द्वारा प्रस्तुत क्वेरी को तुरंत और पूरी तरह से निष्पादित किया जाता है, इसलिए एप्लिकेशन को डेटा जल्दी से प्राप्त होता है।

जब क्वेरी निष्पादित होती है, तो IEnumerable सभी डेटा को लोड करता है, और अगर हमें इसे फ़िल्टर करने की आवश्यकता होती है, तो फ़िल्टरिंग स्वयं क्लाइंट पक्ष पर की जाती है।

IQueryable इंटरफ़ेस System.Linq नेमस्पेस में स्थित है।

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

क्या चुनना है?

यदि आपको लौटाए गए डेटा के पूरे सेट की आवश्यकता है, तो IEnumerable का उपयोग करना बेहतर है, जो अधिकतम गति प्रदान करता है।

यदि आपको लौटाए गए डेटा के पूरे सेट की आवश्यकता नहीं है, लेकिन केवल कुछ फ़िल्टर किए गए डेटा, तो IQueryable का उपयोग करना बेहतर है।


0

उपरोक्त के अलावा, यह नोट करना दिलचस्प है कि आप अपवाद प्राप्त कर सकते हैं यदि आप IQueryableइसके बजाय उपयोग करते हैं IEnumerable:

निम्नलिखित कार्य ठीक है अगर productsएक IEnumerable:

products.Skip(-4);

हालाँकि अगर productsयह एक है IQueryableऔर यह एक डीबी टेबल से रिकॉर्ड एक्सेस करने की कोशिश कर रहा है, तो आपको यह त्रुटि मिलेगी:

OFFSET क्लॉज में निर्दिष्ट ऑफसेट नकारात्मक नहीं हो सकता है।

इसका कारण यह है कि निम्नलिखित क्वेरी का निर्माण किया गया था:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

और OFFSET का नकारात्मक मान नहीं हो सकता।

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