का उपयोग क्या है IQueryable
LINQ के संदर्भ में?
इसका उपयोग विस्तार विधियों या किसी अन्य उद्देश्य के विकास के लिए किया जाता है?
का उपयोग क्या है IQueryable
LINQ के संदर्भ में?
इसका उपयोग विस्तार विधियों या किसी अन्य उद्देश्य के विकास के लिए किया जाता है?
जवाबों:
मार्क ग्रेवेल का जवाब बहुत ही पूर्ण है, लेकिन मुझे लगा कि मैं उपयोगकर्ता के दृष्टिकोण से इस बारे में कुछ जोड़ूंगा, साथ ही ...
उपयोगकर्ता के दृष्टिकोण से मुख्य अंतर यह है कि, जब आप उपयोग करते हैं 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 रिकॉर्ड वापस कर सकते हैं ....
IQueryable<Product>
- जो आपके ओआरएम या रिपॉजिटरी, आदि के लिए विशिष्ट होगा
foreach
, या call का उपयोग करें ToList()
), आप वास्तव में DB को नहीं मारते हैं।
संक्षेप में इसका काम बहुत हद तक 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 " को देखें, ये दोनों इन विषयों का कवरेज प्रदान करते हैं।
हालांकि रीड 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
की SomeServiceClass
LINQ प्रश्नों में एक आम पंक्ति है
where employeesToCollect.Contains(e.PersonId)
थान क्यों
निष्पादन भाग AND (N'M' = [Extent1].[Gender])
में गायब है IEnumerable
, जबकि दोनों फ़ंक्शन कॉलिंग में हमने Where(i => i.Gender == "M") in
program.cs` का उपयोग किया है
अब हम उस बिंदु पर हैं जहां अंतर
IQueryable
और के बीच आयाIEnumerable
जब कोई IQueryable
विधि फ्रैमवर्क करती है तो एक विधि को बुलाया जाता है, यह विधि के अंदर लिखे गए लाइनक स्टेटमेंट को लेती है और यह पता लगाने की कोशिश करती है कि क्या परिणाम पर अधिक लाइनक अभिव्यक्तियों को परिभाषित किया गया है, तो यह तब तक सभी लिनक प्रश्नों को इकट्ठा करता है जब तक कि परिणाम लाने के लिए और अधिक उपयुक्त sql का निर्माण न हो जाए निष्पादित करने के लिए क्वेरी।
यह बहुत सारे लाभ प्रदान करता है जैसे,
उदाहरण के लिए यहाँ sql सर्वर IQueryable निष्पादन के बाद केवल दो पंक्तियों के लिए ही लौटा है लेकिन IEnumerer क्वेरी के लिए तीन पंक्तियों को क्यों लौटाया ?
IEnumerable
विधि के मामले में , इकाई ढांचे ने विधि के अंदर लिखे लाइनक स्टेटमेंट को लिया और जब परिणाम प्राप्त करने की आवश्यकता होती है तो sql क्वेरी का निर्माण करता है। इसमें sql क्वेरी का निर्माण करने के लिए बाकी linq हिस्सा शामिल नहीं है। यहाँ की तरह स्तंभ पर sql सर्वर में कोई फ़िल्टरिंग नहीं की जाती है gender
।
लेकिन आउटपुट एक ही हैं? चूँकि 'IEnumerable sql सर्वर से परिणाम प्राप्त करने के बाद अनुप्रयोग स्तर में परिणाम को फ़िल्टर करता है
तो, किसी को क्या चुनना चाहिए? मैं व्यक्तिगत रूप से फ़ंक्शन परिणाम को परिभाषित करना पसंद करता हूं IQueryable<T>
क्योंकि इसके बहुत सारे लाभ हैंIEnumerable
जैसे कि, आप दो या अधिक IQueryable कार्यों में शामिल हो सकते हैं, जो sql सर्वर पर अधिक विशिष्ट स्क्रिप्ट उत्पन्न करते हैं।
यहाँ उदाहरण में आप IQueryable Query(IQueryableQuery2)
एक अधिक विशिष्ट स्क्रिप्ट उत्पन्न कर सकते हैं , IEnumerable query(IEnumerableQuery2)
जो मेरे दृष्टिकोण से बहुत अधिक स्वीकार्य है।
यह आगे की लाइन को और क्वेरी करने की अनुमति देता है। यदि यह सेवा सीमा से परे था, तो इस IQueryable ऑब्जेक्ट के उपयोगकर्ता को इसके साथ और अधिक करने की अनुमति होगी।
उदाहरण के लिए यदि आप निबर्नेट के साथ आलसी लोडिंग का उपयोग कर रहे थे, तो इसका परिणाम ग्राफ में लोड हो सकता है जब / यदि आवश्यक हो।