Linq के साथ IQueryable का उपयोग करना


256

का उपयोग क्या है IQueryableLINQ के संदर्भ में?

इसका उपयोग विस्तार विधियों या किसी अन्य उद्देश्य के विकास के लिए किया जाता है?

जवाबों:


507

मार्क ग्रेवेल का जवाब बहुत ही पूर्ण है, लेकिन मुझे लगा कि मैं उपयोगकर्ता के दृष्टिकोण से इस बारे में कुछ जोड़ूंगा, साथ ही ...


उपयोगकर्ता के दृष्टिकोण से मुख्य अंतर यह है कि, जब आप उपयोग करते हैं IQueryable<T>(एक प्रदाता जो चीजों को सही ढंग से समर्थन करता है) के साथ, आप बहुत सारे संसाधनों को बचा सकते हैं।

उदाहरण के लिए, यदि आप एक दूरस्थ डेटाबेस के खिलाफ काम कर रहे हैं, तो कई ORM सिस्टम के साथ, आपके पास तालिका से दो तरीके से डेटा लाने का विकल्प होता है, एक जो वापस लौटता है IEnumerable<T>, और एक जो रिटर्न देता है IQueryable<T>। उदाहरण के लिए, आपके पास उत्पाद तालिका है, और आप उन सभी उत्पादों को प्राप्त करना चाहते हैं जिनकी लागत $ 25 है।

यदि तुम करो:

 IEnumerable<Product> products = myORM.GetProducts();
 var productsOver25 = products.Where(p => p.Cost >= 25.00);

यहाँ क्या होता है, क्या डेटाबेस सभी उत्पादों को लोड करता है, और उन्हें तार से आपके प्रोग्राम में भेजता है। आपका प्रोग्राम तब डेटा को फ़िल्टर करता है। संक्षेप में, डेटाबेस एक करता है SELECT * FROM Products, और आपको हर उत्पाद लौटाता है।

सही IQueryable<T>प्रदाता के साथ, दूसरी ओर, आप कर सकते हैं:

 IQueryable<Product> products = myORM.GetQueryableProducts();
 var productsOver25 = products.Where(p => p.Cost >= 25.00);

कोड समान दिखता है, लेकिन यहां अंतर यह है कि SQL निष्पादित होगा SELECT * FROM Products WHERE Cost >= 25

एक डेवलपर के रूप में आपके POV से, यह समान दिखता है। हालाँकि, प्रदर्शन के दृष्टिकोण से, आप केवल 20,000 के बजाय नेटवर्क में 2 रिकॉर्ड वापस कर सकते हैं ....


9
"GetQueryableProducts ()?" की परिभाषा कहाँ है?
पंकज

12
@StackOverflowUser यह किसी भी तरीके से होने का इरादा रखता है IQueryable<Product>- जो आपके ओआरएम या रिपॉजिटरी, आदि के लिए विशिष्ट होगा
रीड

सही। लेकिन आपने उल्लेख किया कि इस फंक्शन कॉल के बाद क्लॉज कहां है। इसलिए सिस्टम अभी भी फ़िल्टर से अनजान है। मेरा मतलब है कि यह अभी भी उत्पादों के सभी रिकॉर्ड प्राप्त करता है। सही?
पंकज

@StackOverflowUser नहीं - यह कि IQueryable <T> की सुंदरता है - यह मूल्यांकन करने के लिए सेटअप किया जा सकता है जब आप परिणाम प्राप्त करते हैं - जिसका अर्थ है कि इस तथ्य के बाद उपयोग किए गए कहां खंड, अभी भी सर्वर पर चलने वाले SQL कथन में अनुवादित हो जाएगा, और तार में केवल आवश्यक तत्वों को खींचो ...
रीड कोपसे डिके

40
@Testing आप वास्तव में अभी भी DB में नहीं जा रहे हैं। जब तक आप वास्तव में परिणामों की गणना नहीं करते (यानी: a foreach, या call का उपयोग करें ToList()), आप वास्तव में DB को नहीं मारते हैं।
रीड कोपसे

188

संक्षेप में इसका काम बहुत हद तक IEnumerable<T>एक क्वेरी डेटा स्रोत का प्रतिनिधित्व करने के समान है - यह अंतर कि विभिन्न LINQ विधियाँ (पर Queryable) Expressionडेलिगेट्स (जो Enumerableउपयोग करता है) के बजाय पेड़ों का उपयोग करके क्वेरी बनाने के लिए अधिक विशिष्ट हो सकती हैं ।

अभिव्यक्ति के पेड़ों को आपके चुने हुए LINQ प्रदाता द्वारा निरीक्षण किया जा सकता है और वास्तविक क्वेरी में बदल दिया जा सकता है - हालांकि यह अपने आप में एक काली कला है।

यह वास्तव में नीचे है ElementType, Expressionऔर Provider- लेकिन वास्तव में आपको शायद ही कभी एक उपयोगकर्ता के रूप में इस बारे में परवाह करने की आवश्यकता है । केवल LINQ कार्यान्वयनकर्ता को gory विवरण जानना आवश्यक है।


टिप्पणी फिर से; मुझे बिल्कुल यकीन नहीं है कि आप उदाहरण के माध्यम से क्या चाहते हैं, लेकिन LINQ-to-SQL पर विचार करें; यहाँ केंद्रीय वस्तु एक है DataContext, जो हमारे डेटाबेस-आवरण का प्रतिनिधित्व करती है। इसमें आमतौर पर प्रति तालिका (उदाहरण के लिए Customers), और तालिका लागू करने वाली एक संपत्ति होती है IQueryable<Customer>। लेकिन हम इतना सीधा उपयोग नहीं करते हैं; विचार करें:

using(var ctx = new MyDataContext()) {
    var qry = from cust in ctx.Customers
              where cust.Region == "North"
              select new { cust.Id, cust.Name };
    foreach(var row in qry) {
        Console.WriteLine("{0}: {1}", row.Id, row.Name);
    }
}

यह (सी # संकलक द्वारा) बन जाता है:

var qry = ctx.Customers.Where(cust => cust.Region == "North")
                .Select(cust => new { cust.Id, cust.Name });

जिसे फिर से (सी # संकलक द्वारा) के रूप में व्याख्या की गई है:

var qry = Queryable.Select(
              Queryable.Where(
                  ctx.Customers,
                  cust => cust.Region == "North"),
              cust => new { cust.Id, cust.Name });

महत्वपूर्ण रूप से, Queryableअभिव्यक्ति के पेड़ पर स्थिर तरीके , जो - नियमित आईएल के बजाय, एक ऑब्जेक्ट मॉडल के लिए संकलित हो जाते हैं। उदाहरण के लिए - बस "जहां" को देख रहे हैं, यह हमें कुछ तुलनीय देता है:

var cust = Expression.Parameter(typeof(Customer), "cust");
var lambda = Expression.Lambda<Func<Customer,bool>>(
                  Expression.Equal(
                      Expression.Property(cust, "Region"),
                      Expression.Constant("North")
                  ), cust);

... Queryable.Where(ctx.Customers, lambda) ...

क्या कंपाइलर ने हमारे लिए बहुत कुछ नहीं किया? इस ऑब्जेक्ट मॉडल को अलग किया जा सकता है, इसका क्या अर्थ है के लिए निरीक्षण किया गया है, और TSQL जनरेटर द्वारा फिर से एक साथ वापस रखा गया है - कुछ इस तरह दे:

 SELECT c.Id, c.Name
 FROM [dbo].[Customer] c
 WHERE c.Region = 'North'

(स्ट्रिंग एक पैरामीटर के रूप में समाप्त हो सकती है; मुझे याद नहीं है)

अगर हम सिर्फ एक प्रतिनिधि का इस्तेमाल करते हैं तो यह संभव नहीं होगा। और यहQueryable / का बिंदु है IQueryable<T>: यह अभिव्यक्ति पेड़ों का उपयोग करने के लिए प्रवेश-बिंदु प्रदान करता है।

यह सब बहुत जटिल है, इसलिए यह एक अच्छा काम है कि कंपाइलर हमारे लिए अच्छा और आसान बनाता है।

अधिक जानकारी के लिए, " गहराई में C # " या " लड़ाई में LINQ " को देखें, ये दोनों इन विषयों का कवरेज प्रदान करते हैं।


2
यदि आपको कोई आपत्ति नहीं है, तो आप मुझे सरल समझने योग्य उदाहरण के साथ अपडेट कर सकते हैं (यदि आपके पास समय है)।
15:

क्या आप समझा सकते हैं "गेटएकेबल प्रोडक्शंस () की परिभाषा कहां है; "?" श्री रीड कोपसी के उत्तर में
पंकज

एक क्वेरी में अनुवाद करने की अभिव्यक्तियों की पंक्ति का आनंद लिया "अपने आप में काली कला है" ... सच्चाई का एक बहुत
afreeland

यदि आपने एक प्रतिनिधि का उपयोग किया है तो यह क्यों संभव नहीं होगा?
डेविड क्लेम्फनर

1
@Backwards_Dave क्योंकि IL को एक प्रतिनिधि बिंदु (अनिवार्य रूप से), और IL, SQL बनाने के लिए पर्याप्त रूप से इरादे के पुनर्निर्माण की कोशिश करने के लिए इसे उचित बनाने के लिए पर्याप्त रूप से अभिव्यंजक नहीं है। आईएल भी अनुमति देता है भी कई यानी सबसे चीजें हैं जो आईएल में व्यक्त किया जा सकता है - बातें नहीं कर सकता सीमित वाक्य रचना में व्यक्त किया जा है कि यह SQL जैसी चीजों में बदलने के लिए उचित है
मार्क Gravell

17

हालांकि रीड Copsey और मार्क Gravell पहले से ही के बारे में वर्णन किया गया IQueryableहै (और भी IEnumerable) पर्याप्त, एमआई पर एक छोटा सा उदाहरण प्रदान करके छोटे से अधिक यहाँ जोड़ना चाहते हैं IQueryableऔर IEnumerableके रूप में कई उपयोगकर्ताओं के लिए यह करने के लिए कहा

उदाहरण : मैंने डेटाबेस में दो तालिकाएँ बनाई हैं

   CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL)
   CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL)

PersonIdतालिका की प्राथमिक कुंजी ( ) तालिका Employeeकी एक फोर्जिन कुंजी ( personid) भी हैPerson

इसके बाद मैंने अपने आवेदन में ado.net एंटिटी मॉडल जोड़ा और उस पर सर्विस क्लास के नीचे बनाया

public class SomeServiceClass
{   
    public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect)
    {
        DemoIQueryableEntities db = new DemoIQueryableEntities();
        var allDetails = from Employee e in db.Employees
                         join Person p in db.People on e.PersonId equals p.PersonId
                         where employeesToCollect.Contains(e.PersonId)
                         select e;
        return allDetails;
    }

    public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect)
    {
        DemoIQueryableEntities db = new DemoIQueryableEntities();
        var allDetails = from Employee e in db.Employees
                         join Person p in db.People on e.PersonId equals p.PersonId
                         where employeesToCollect.Contains(e.PersonId)
                         select e;
        return allDetails;
    }
}

वे एक ही linq शामिल हैं। इसे program.csनीचे परिभाषित के रूप में कहा जाता है

class Program
{
    static void Main(string[] args)
    {
        SomeServiceClass s= new SomeServiceClass(); 

        var employeesToCollect= new []{0,1,2,3};

        //IQueryable execution part
        var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M");            
        foreach (var emp in IQueryableList)
        {
            System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
        }
        System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count());

        //IEnumerable execution part
        var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M");
        foreach (var emp in IEnumerableList)
        {
           System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
        }
        System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count());

        Console.ReadKey();
    }
}

आउटपुट स्पष्ट रूप से दोनों के लिए समान है

ID:1, EName:Ken,Gender:M  
ID:3, EName:Roberto,Gender:M  
IQueryable contain 2 row in result set  
ID:1, EName:Ken,Gender:M  
ID:3, EName:Roberto,Gender:M  
IEnumerable contain 2 row in result set

तो सवाल यह है कि क्या / कहाँ अंतर है? यह कोई अंतर नहीं लगता है सही है? वास्तव में!!

आइए इन अवधि के दौरान इकाई framwork 5 द्वारा उत्पन्न और निष्पादित साइकल प्रश्नों पर एक नजर डालते हैं

I निष्पादन योग्य निष्पादन भाग

--IQueryableQuery1 
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])

--IQueryableQuery2
SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Employee] AS [Extent1]
    WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
)  AS [GroupBy1]

IEnumerable निष्पादन हिस्सा

--IEnumerableQuery1
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)

--IEnumerableQuery2
SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)

दोनों निष्पादन भाग के लिए सामान्य स्क्रिप्ट

/* these two query will execute for both IQueryable or IEnumerable to get details from Person table
   Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable
--ICommonQuery1 
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

--ICommonQuery2
exec sp_executesql N'SELECT 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
*/

तो अब आपके पास कुछ सवाल हैं, मैं उन लोगों का अनुमान लगाता हूं और उन्हें जवाब देने की कोशिश करता हूं

समान परिणाम के लिए अलग-अलग स्क्रिप्ट क्यों बनाई गई हैं?

आइए जानें कुछ बिंदु यहां,

सभी प्रश्नों का एक सामान्य भाग है

WHERE [Extent1].[PersonId] IN (0,1,2,3)

क्यों? दोनों समारोह क्योंकि IQueryable<Employee> GetEmployeeAndPersonDetailIQueryableऔर IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerableकी SomeServiceClassLINQ प्रश्नों में एक आम पंक्ति है

where employeesToCollect.Contains(e.PersonId)

थान क्यों निष्पादन भाग AND (N'M' = [Extent1].[Gender])में गायब है IEnumerable, जबकि दोनों फ़ंक्शन कॉलिंग में हमने Where(i => i.Gender == "M") inprogram.cs` का उपयोग किया है

अब हम उस बिंदु पर हैं जहां अंतर IQueryableऔर के बीच आयाIEnumerable

जब कोई IQueryableविधि फ्रैमवर्क करती है तो एक विधि को बुलाया जाता है, यह विधि के अंदर लिखे गए लाइनक स्टेटमेंट को लेती है और यह पता लगाने की कोशिश करती है कि क्या परिणाम पर अधिक लाइनक अभिव्यक्तियों को परिभाषित किया गया है, तो यह तब तक सभी लिनक प्रश्नों को इकट्ठा करता है जब तक कि परिणाम लाने के लिए और अधिक उपयुक्त sql का निर्माण न हो जाए निष्पादित करने के लिए क्वेरी।

यह बहुत सारे लाभ प्रदान करता है जैसे,

  • केवल उन पंक्तियों को sql सर्वर द्वारा पॉप्युलेट किया गया है जो संपूर्ण linq क्वेरी निष्पादन द्वारा मान्य हो सकती हैं
  • अनावश्यक पंक्तियों का चयन न करके SQL सर्वर प्रदर्शन में मदद करता है
  • नेटवर्क लागत कम हो

उदाहरण के लिए यहाँ sql सर्वर IQueryable निष्पादन के बाद केवल दो पंक्तियों के लिए ही लौटा है लेकिन IEnumerer क्वेरी के लिए तीन पंक्तियों को क्यों लौटाया ?

IEnumerableविधि के मामले में , इकाई ढांचे ने विधि के अंदर लिखे लाइनक स्टेटमेंट को लिया और जब परिणाम प्राप्त करने की आवश्यकता होती है तो sql क्वेरी का निर्माण करता है। इसमें sql क्वेरी का निर्माण करने के लिए बाकी linq हिस्सा शामिल नहीं है। यहाँ की तरह स्तंभ पर sql सर्वर में कोई फ़िल्टरिंग नहीं की जाती है gender

लेकिन आउटपुट एक ही हैं? चूँकि 'IEnumerable sql सर्वर से परिणाम प्राप्त करने के बाद अनुप्रयोग स्तर में परिणाम को फ़िल्टर करता है

तो, किसी को क्या चुनना चाहिए? मैं व्यक्तिगत रूप से फ़ंक्शन परिणाम को परिभाषित करना पसंद करता हूं IQueryable<T>क्योंकि इसके बहुत सारे लाभ हैंIEnumerable जैसे कि, आप दो या अधिक IQueryable कार्यों में शामिल हो सकते हैं, जो sql सर्वर पर अधिक विशिष्ट स्क्रिप्ट उत्पन्न करते हैं।

यहाँ उदाहरण में आप IQueryable Query(IQueryableQuery2)एक अधिक विशिष्ट स्क्रिप्ट उत्पन्न कर सकते हैं , IEnumerable query(IEnumerableQuery2)जो मेरे दृष्टिकोण से बहुत अधिक स्वीकार्य है।


2

यह आगे की लाइन को और क्वेरी करने की अनुमति देता है। यदि यह सेवा सीमा से परे था, तो इस IQueryable ऑब्जेक्ट के उपयोगकर्ता को इसके साथ और अधिक करने की अनुमति होगी।

उदाहरण के लिए यदि आप निबर्नेट के साथ आलसी लोडिंग का उपयोग कर रहे थे, तो इसका परिणाम ग्राफ में लोड हो सकता है जब / यदि आवश्यक हो।

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