c# - रिटर्निंग IENumerable<टी> बनाम IQueryable<टी>




linq linq-to-sql (10)

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

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

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

संदर्भ Link

IQueryable<T> बनाम IQueryable<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;

क्या दोनों स्थगित निष्पादन होंगे और किसी को दूसरे पर कब प्राथमिकता दी जानी चाहिए?


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


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

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

एक आईनेमरेबल लौटने से स्वचालित रूप से रनटाइम को आपके संग्रह से पूछने के लिए LINQ से ऑब्जेक्ट्स का उपयोग करने के लिए मजबूर किया जाएगा।

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


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

IENumerable इंटरफ़ेस सिस्टम में है। चयन नामस्थान।

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

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

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

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

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

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

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


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

(1) IQueryable IEnumerable इंटरफ़ेस बढ़ाता है। (आप किसी चीज़ के लिए एक IQueryable भेज सकते हैं जो त्रुटि के बिना IEnumerable अपेक्षा करता है।)

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

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

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

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

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

(सी) डेटा सेट के रिकॉर्ड, बच्चे की संस्थाओं और दृश्यों से संपत्तियों को खत्म करें और एक्सेस करें। (अपवाद का कारण बन सकता है!)

संभावित मुद्दे

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

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

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

उपाय

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

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

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


मैंने हाल ही में आईनुमरेबल बनाम IQueryable के साथ एक मुद्दा में भाग लिया। उपयोग किए जाने वाले एल्गोरिदम ने परिणामों का एक सेट प्राप्त करने के लिए पहले एक IQueryable क्वेरी का प्रदर्शन किया। फिर उन्हें ईएफ वर्ग के रूप में तत्काल वस्तुओं के साथ एक फोरैच पाश में पास कर दिया गया। इस ईएफ वर्ग का उपयोग तब एक लिंक से एंटिटी क्वेरी के खंड में किया गया था, जिससे परिणाम आईनेमरेबल हो गया था। मैं संस्थाओं के लिए ईएफ और लिंक के लिए बिल्कुल नया हूं, इसलिए यह पता लगाने में थोड़ी देर लग गई कि बाधा क्या थी। मिनीप्रोफिलिंग का उपयोग करके, मुझे क्वेरी मिली और फिर सभी व्यक्तिगत संचालन को एक क्वेरी I क्वेरी के लिए एक IQueryable लिंक में परिवर्तित कर दिया। IENumerable 15 सेकंड लिया और IQueryable निष्पादित करने के लिए 0.5 सेकंड लिया। इसमें तीन टेबल शामिल थे और, इसे पढ़ने के बाद, मेरा मानना ​​है कि आईनेमरेबल क्वेरी वास्तव में तीन टेबल क्रॉस-प्रोडक्ट बना रही थी और परिणामों को फ़िल्टर कर रही थी।

IQueryables को नियम-थंब के रूप में उपयोग करने का प्रयास करें और अपने कार्यों को मापने योग्य बनाने के लिए अपना काम प्रोफ़ाइल बनाएं।


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

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

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

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

उदाहरण के लिए Where(x => x.City == "<City>") और IQueryable दोनों पर काम करता है

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

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

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

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


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

यदि हम गहराई से खोदते हैं और स्रोतों को देखते हैं, तो हम देख सकते हैं कि 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 स्रोत में निर्दिष्ट है।


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

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

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

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


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

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

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

कोड में:

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

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

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

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





iqueryable