การใช้งานIQueryable
ในบริบทของ LINQ คืออะไร?
มันใช้สำหรับการพัฒนาวิธีการขยายหรือวัตถุประสงค์อื่น ๆ ?
การใช้งานIQueryable
ในบริบทของ LINQ คืออะไร?
มันใช้สำหรับการพัฒนาวิธีการขยายหรือวัตถุประสงค์อื่น ๆ ?
คำตอบ:
คำตอบของ Marc Gravellนั้นสมบูรณ์มาก แต่ฉันคิดว่าฉันจะเพิ่มบางอย่างเกี่ยวกับสิ่งนี้จากมุมมองของผู้ใช้เช่นกัน ...
ความแตกต่างที่สำคัญจากมุมมองของผู้ใช้คือเมื่อคุณใช้IQueryable<T>
(กับผู้ให้บริการที่สนับสนุนสิ่งต่าง ๆ อย่างถูกต้อง) คุณสามารถประหยัดทรัพยากรได้มาก
ตัวอย่างเช่นถ้าคุณกำลังทำงานกับฐานข้อมูลระยะไกลด้วยระบบการออมจำนวนมากคุณมีตัวเลือกในการเรียกข้อมูลจากตารางในสองวิธีหนึ่งซึ่งผลตอบแทนและหนึ่งที่ส่งกลับ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
ดำเนินการจะเป็น
จากมุมมองของคุณในฐานะนักพัฒนาดูเหมือนกัน อย่างไรก็ตามจากมุมมองด้านประสิทธิภาพคุณสามารถส่งคืนได้ 2 ระเบียนในเครือข่ายแทนที่จะเป็น 20,000 ....
IQueryable<Product>
- จะเฉพาะกับ ORM หรือพื้นที่เก็บข้อมูลของคุณ ฯลฯ
foreach
หรือโทรToList()
) จริง ๆ คุณจะไม่ได้กดฐานข้อมูล
ในสาระสำคัญหน้าที่ของมันคล้ายกับIEnumerable<T>
- เพื่อแสดงแหล่งข้อมูลที่Queryable
สามารถสืบค้นได้- ความแตกต่างที่ว่าวิธีการ LINQ (เปิด) ต่างๆนั้นมีความเฉพาะเจาะจงมากขึ้นเพื่อสร้างแบบสอบถามโดยใช้Expression
แผนผังแทนที่จะเป็นตัวแทน (ซึ่งเป็นสิ่งที่Enumerable
ใช้)
ทรีนิพจน์สามารถตรวจสอบได้โดยผู้ให้บริการ LINQ ที่คุณเลือกและเปลี่ยนเป็นข้อความค้นหาจริง - แม้ว่าจะเป็นงานศิลปะสีดำในตัวเอง
นี้เป็นจริงลงไปElementType
, Expression
และProvider
- แต่ในความเป็นจริงคุณไม่ค่อยจำเป็นต้องดูแลเกี่ยวกับเรื่องนี้ในฐานะที่เป็นผู้ใช้ ผู้ใช้งาน LINQ เท่านั้นที่ต้องรู้รายละเอียดของเลือด
ความคิดเห็นเรื่อง; ฉันไม่แน่ใจว่าสิ่งที่คุณต้องการเป็นตัวอย่าง แต่พิจารณา LINQ-to-SQL; วัตถุศูนย์กลางนี่คือ a DataContext
, ซึ่งหมายถึง wrapper ฐานข้อมูลของเรา นี้มักจะมีคุณสมบัติต่อโต๊ะ (ตัวอย่าง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);
}
}
สิ่งนี้จะกลายเป็น (โดยคอมไพเลอร์ C #):
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
ซึ่งตีความอีกครั้ง (โดยคอมไพเลอร์ C #) เป็น:
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
ที่สำคัญวิธีการแบบคงที่ในการQueryable
ใช้ต้นไม้การแสดงออกซึ่ง - แทนที่จะรวบรวม IL ปกติเพื่อรวบรวมรูปแบบวัตถุ ตัวอย่างเช่น - เพียงดูที่ "ที่ไหน" สิ่งนี้ทำให้เรามีสิ่งที่เทียบเคียงได้กับ:
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 # in Depth " หรือ " LINQ in Action " ซึ่งทั้งสองอย่างนี้ให้ความคุ้มครองหัวข้อเหล่านี้
แม้ว่าReed CopseyและMarc Gravellได้อธิบายไว้แล้วเกี่ยวกับIQueryable
(และIEnumerable
) เพียงพอ mI ต้องการเพิ่มอีกเล็กน้อยที่นี่โดยให้ตัวอย่างเล็ก ๆ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
เป็นคีย์ forgein ( 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
ดังนั้นคำถามคืออะไรแตกต่างกันที่ไหน? ดูเหมือนว่าจะไม่มีความแตกต่างใช่ไหม? จริงๆ!!
ลองมาดูในการค้นหา sql สร้างและดำเนินการโดยนิติบุคคล framwork 5 ในช่วงเวลาเหล่านี้
ส่วนการดำเนินการ IQueryable
--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
Framwork เอนทิตีอะไรที่ทำเมื่อIQueryable
วิธีการที่เรียกมันใช้คำสั่ง linq เขียนภายในวิธีการและพยายามที่จะหาว่าการแสดงออก linq มากขึ้นมีการกำหนดไว้ใน resultset แล้วมันรวบรวมแบบสอบถามสอบถามทั้งหมด linq ที่กำหนดไว้จนกว่าผลต้องดึงและสร้าง sql ที่เหมาะสมมากขึ้น แบบสอบถามเพื่อดำเนินการ
มันให้ประโยชน์มากมายเช่น
เช่นที่นี่ในตัวอย่างเซิร์ฟเวอร์ sql กลับไปที่แอปพลิเคชันเพียงสองแถวหลังจากการดำเนินการ IQueryable` แต่ส่งคืนแถวที่สามสำหรับการค้นหา IEnumerable ทำไม?
ในกรณีของ IEnumerable
วิธีการกรอบงานเอนทิตีเอาคำสั่ง linq เขียนไว้ในวิธีการและสร้างแบบสอบถาม SQL เมื่อผลต้องดึง มันไม่รวมส่วนที่เหลือ linq เพื่อสร้างแบบสอบถาม sql เช่นเดียวกับที่นี่ไม่มีการกรองจะทำในเซิร์ฟเวอร์ SQL gender
ในคอลัมน์
แต่ผลลัพธ์เหมือนกันหรือไม่ เนื่องจาก 'IEnumerable กรองผลลัพธ์เพิ่มเติมในระดับแอปพลิเคชันหลังจากเรียกผลลัพธ์จากเซิร์ฟเวอร์ sql
ดังนั้นสิ่งที่คนควรเลือก? ฉันเองต้องการกำหนดผลลัพธ์ของฟังก์ชันIQueryable<T>
เนื่องจากมีประโยชน์มากมายที่มีมากกว่าIEnumerable
คุณสามารถเข้าร่วมฟังก์ชัน IQueryable สองฟังก์ชันขึ้นไปซึ่งสร้างสคริปต์ที่เจาะจงมากขึ้นไปยังเซิร์ฟเวอร์ sql
ในตัวอย่างนี้คุณสามารถเห็นการIQueryable Query(IQueryableQuery2)
สร้างสคริปต์เฉพาะเจาะจงมากกว่าIEnumerable query(IEnumerableQuery2)
ที่ยอมรับได้มากในมุมมองของฉัน
จะช่วยให้การสอบถามเพิ่มเติมลงไปที่บรรทัด ถ้าสิ่งนี้อยู่นอกเหนือขอบเขตการให้บริการบอกว่าผู้ใช้ของวัตถุ IQueryable นี้จะได้รับอนุญาตให้ทำอะไรได้มากกว่ากับมัน
ตัวอย่างเช่นหากคุณใช้การโหลดแบบสันหลังยาวกับ nhibernate อาจส่งผลให้กราฟถูกโหลดเมื่อ / หากจำเป็น