इस बारे में बहुत कुछ कहा जा सकता है। मुझ पर ध्यान केंद्रित करते हैं AsEnumerable
और AsQueryable
और उल्लेख ToList()
रास्ते।
ये तरीके क्या करते हैं?
AsEnumerable
और क्रमशः AsQueryable
कास्ट या कन्वर्ट IEnumerable
या IQueryable
। मैं कहता हूं कि एक कारण के साथ कास्ट या कन्वर्ट करें:
जब स्रोत ऑब्जेक्ट पहले से ही लक्ष्य इंटरफ़ेस को लागू करता है, तो स्रोत ऑब्जेक्ट स्वयं ही वापस आ जाता है, लेकिन लक्ष्य इंटरफ़ेस में डाला जाता है। दूसरे शब्दों में: प्रकार नहीं बदला गया है, लेकिन संकलन-समय प्रकार है।
जब स्रोत ऑब्जेक्ट लक्ष्य इंटरफ़ेस को लागू नहीं करता है, तो स्रोत ऑब्जेक्ट एक ऑब्जेक्ट में परिवर्तित हो जाता है जो लक्ष्य इंटरफ़ेस को लागू करता है। तो प्रकार और संकलन-समय दोनों प्रकार बदल दिए जाते हैं।
इसे मैं कुछ उदाहरणों के साथ दिखाता हूं। मुझे यह छोटा सा तरीका मिल गया है जो संकलन-समय प्रकार और एक वस्तु के वास्तविक प्रकार की रिपोर्ट करता है ( सौजन्य जॉन स्कीट ):
void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
आइए एक मनमाना linq-to-sql आज़माएं Table<T>
, जो लागू होता है IQueryable
:
ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());
परिणाम:
Compile-time type: Table`1
Actual type: Table`1
Compile-time type: IEnumerable`1
Actual type: Table`1
Compile-time type: IQueryable`1
Actual type: Table`1
आप देखते हैं कि तालिका का वर्ग हमेशा ही वापस आ जाता है, लेकिन इसका प्रतिनिधित्व बदल जाता है।
अब एक वस्तु जो लागू होती है IEnumerable
, नहीं IQueryable
:
var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());
परिणाम:
Compile-time type: Int32[]
Actual type: Int32[]
Compile-time type: IEnumerable`1
Actual type: Int32[]
Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1
वो रहा। AsQueryable()
सरणी को एक में परिवर्तित कर दिया है EnumerableQuery
, जो " डेटा स्रोत के IEnumerable<T>
रूप में एक संग्रह का प्रतिनिधित्व करता है IQueryable<T>
।" (MSDN)।
क्या फायदा?
AsEnumerable
अक्सर किसी भी IQueryable
कार्यान्वयन से LINQ को ऑब्जेक्ट्स (L2O) पर स्विच करने के लिए उपयोग किया जाता है, ज्यादातर इसलिए क्योंकि पूर्व L2O के कार्यों का समर्थन नहीं करता है। अधिक जानकारी के लिए देखें LINQ इकाई पर AsEnumerable () का क्या प्रभाव है? ।
उदाहरण के लिए, एक एंटिटी फ्रेमवर्क क्वेरी में हम केवल एक सीमित संख्या में विधियों का उपयोग कर सकते हैं। इसलिए यदि, उदाहरण के लिए, हमें अपने स्वयं के तरीकों में से एक का उपयोग करने की आवश्यकता है, तो हम आमतौर पर ऐसा कुछ लिखेंगे
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => MySuperSmartMethod(x))
ToList
- जो IEnumerable<T>
कि a को a में परिवर्तित करता है List<T>
- अक्सर इस उद्देश्य के लिए भी उपयोग किया जाता है। AsEnumerable
बनाम का उपयोग करने का लाभ यह ToList
है कि AsEnumerable
क्वेरी निष्पादित नहीं करता है। AsEnumerable
आस्थगित निष्पादन को संरक्षित करता है और अक्सर बेकार मध्यवर्ती सूची का निर्माण नहीं करता है।
दूसरी ओर, जब LINQ क्वेरी का जबरन निष्पादन वांछित होता है, ToList
तो ऐसा करने का एक तरीका हो सकता है।
AsQueryable
LINQ स्टेटमेंट में एक संग्रहणीय संग्रह स्वीकार अभिव्यक्ति बनाने के लिए इस्तेमाल किया जा सकता है। अधिक विवरण के लिए यहां देखें: क्या मुझे वास्तव में संग्रह पर AsQueryable () का उपयोग करने की आवश्यकता है? ।
मादक द्रव्यों के सेवन पर ध्यान दें!
AsEnumerable
दवा की तरह काम करता है। यह एक त्वरित सुधार है, लेकिन एक लागत पर और यह अंतर्निहित समस्या का समाधान नहीं करता है।
कई स्टैक ओवरफ्लो उत्तरों में, मैं लोगों AsEnumerable
को LINQ अभिव्यक्तियों में असमर्थित तरीकों के साथ किसी भी समस्या को ठीक करने के लिए आवेदन करने के लिए देखता हूं । लेकिन कीमत हमेशा स्पष्ट नहीं होती है। उदाहरण के लिए, यदि आप ऐसा करते हैं:
context.MyLongWideTable // A table with many records and columns
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate })
... सब कुछ बड़े करीने से SQL कथन में फ़िल्टर किया गया है ( Where
) और प्रोजेक्ट ( Select
)। यानी, SQL परिणाम सेट की लंबाई और चौड़ाई, क्रमशः, दोनों कम हो जाते हैं।
अब मान लें कि उपयोगकर्ता केवल तारीख का भाग देखना चाहते हैं CreateDate
। एंटिटी फ्रेमवर्क में आपको जल्दी पता चलेगा कि ...
.Select(x => new { x.Name, x.CreateDate.Date })
... समर्थित नहीं है (लेखन के समय)। आह, सौभाग्य से वहाँ AsEnumerable
तय है:
context.MyLongWideTable.AsEnumerable()
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate.Date })
ज़रूर चलता है, शायद। लेकिन यह पूरी तालिका को स्मृति में खींचता है और फिर फ़िल्टर और अनुमानों को लागू करता है। ठीक है, ज्यादातर लोग Where
पहले करने के लिए काफी स्मार्ट हैं :
context.MyLongWideTable
.Where(x => x.Type == "type").AsEnumerable()
.Select(x => new { x.Name, x.CreateDate.Date })
लेकिन फिर भी सभी कॉलम पहले प्राप्त किए जाते हैं और प्रक्षेपण स्मृति में किया जाता है।
असली तय यह है:
context.MyLongWideTable
.Where(x => x.Type == "type")
.Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })
(लेकिन इसके लिए बस थोड़ा सा और ज्ञान चाहिए ...)
ये तरीके क्या नहीं करते हैं?
IQueryable क्षमताओं को पुनर्स्थापित करें
अब एक महत्वपूर्ण चेतावनी। जब तुम करोगे
context.Observations.AsEnumerable()
.AsQueryable()
आप के रूप में प्रतिनिधित्व स्रोत वस्तु के साथ समाप्त होगा IQueryable
। (क्योंकि दोनों विधियाँ केवल डाली जाती हैं और परिवर्तित नहीं होती हैं)।
लेकिन जब आप करते हैं
context.Observations.AsEnumerable().Select(x => x)
.AsQueryable()
परिणाम क्या होगा?
Select
एक का उत्पादन WhereSelectEnumerableIterator
। यह एक आंतरिक .Net वर्ग है जो लागू होता है IEnumerable
, नहींIQueryable
। इसलिए दूसरे प्रकार में रूपांतरण हुआ है और बाद में AsQueryable
कभी भी मूल स्रोत वापस नहीं आ सकता है।
इस का निहितार्थ यह है कि उपयोग कर रहा है AsQueryable
है जादुई इंजेक्षन एक प्रश्न प्रदाता के लिए एक रास्ता नहीं अपनी विशिष्ट के साथ एक गणनीय में पेश करता है। मान लीजिए आप करते हैं
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => x.ToString())
.AsQueryable()
.Where(...)
जहां स्थिति एसक्यूएल में अनुवाद नहीं किया जाएगा। AsEnumerable()
LINQ कथनों के बाद निश्चित रूप से एंटिटी फ्रेमवर्क क्वेरी प्रदाता के साथ कनेक्शन काट देता है।
मैं जानबूझकर इस उदाहरण को दिखाता हूं क्योंकि मैंने यहां ऐसे प्रश्न देखे हैं जहां उदाहरण के लिए लोग Include
कॉल करके संग्रह में 'इंजेक्शन' लगाने की कोशिश करते हैं AsQueryable
। यह संकलित करता है और चलाता है, लेकिन यह कुछ भी नहीं करता है क्योंकि अंतर्निहित वस्तु का Include
अब कार्यान्वयन नहीं है।
निष्पादित
दोनों AsQueryable
और AsEnumerable
निष्पादित नहीं है (या गणना ) स्रोत ऑब्जेक्ट। वे केवल अपने प्रकार या प्रतिनिधित्व को बदलते हैं। दोनों में इंटरफेस था, IQueryable
और IEnumerable
, "होने के लिए इंतजार कर रहे एक गणना" के अलावा कुछ भी नहीं है। इससे पहले कि वे ऐसा करने के लिए मजबूर कर रहे हैं, उदाहरण के लिए, कॉल करके, उन्हें निष्पादित नहीं किया जाता है ToList()
।
इसका मतलब है कि किसी ऑब्जेक्ट पर IEnumerable
कॉल करके प्राप्त किया गया निष्पादन, अंतर्निहित निष्पादित करेगा । वसीयत का एक बाद का निष्पादन फिर से निष्पादित करेगा । जो बहुत महंगा हो सकता है।AsEnumerable
IQueryable
IQueryable
IEnumerable
IQueryable
विशिष्ट कार्यान्वयन
अब तक, यह केवल Queryable.AsQueryable
और Enumerable.AsEnumerable
विस्तार विधियों के बारे में था । लेकिन निश्चित रूप से कोई भी एक ही नाम (और कार्यों) के साथ उदाहरण के तरीकों या विस्तार विधियों को लिख सकता है।
वास्तव में, एक विशिष्ट AsEnumerable
विस्तार विधि का एक सामान्य उदाहरण है DataTableExtensions.AsEnumerable
। DataTable
लागू नहीं करता है IQueryable
या IEnumerable
, इसलिए नियमित विस्तार विधियाँ लागू नहीं होती हैं।