Việc sử dụng IQueryable
trong bối cảnh của LINQ là gì?
Nó được sử dụng để phát triển các phương pháp mở rộng hoặc bất kỳ mục đích nào khác?
Việc sử dụng IQueryable
trong bối cảnh của LINQ là gì?
Nó được sử dụng để phát triển các phương pháp mở rộng hoặc bất kỳ mục đích nào khác?
Câu trả lời:
Câu trả lời của Marc Gravell rất đầy đủ, nhưng tôi nghĩ tôi cũng muốn thêm điều gì đó từ quan điểm của người dùng, ...
Sự khác biệt chính, từ quan điểm của người dùng, là, khi bạn sử dụng IQueryable<T>
(với một nhà cung cấp hỗ trợ mọi thứ một cách chính xác), bạn có thể tiết kiệm rất nhiều tài nguyên.
Ví dụ: nếu bạn đang làm việc với cơ sở dữ liệu từ xa, với nhiều hệ thống ORM, bạn có tùy chọn tìm nạp dữ liệu từ một bảng theo hai cách, một trả về IEnumerable<T>
và một trả về một IQueryable<T>
. Ví dụ: bạn có bảng Sản phẩm và bạn muốn nhận tất cả các sản phẩm có chi phí> $ 25.
Nếu bạn làm:
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Điều xảy ra ở đây, là cơ sở dữ liệu tải tất cả các sản phẩm và chuyển chúng qua dây cho chương trình của bạn. Chương trình của bạn sau đó lọc dữ liệu. Về bản chất, cơ sở dữ liệu thực hiện SELECT * FROM Products
và trả về MỌI sản phẩm cho bạn.
Với IQueryable<T>
nhà cung cấp phù hợp , mặt khác, bạn có thể làm:
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Mã trông giống nhau, nhưng sự khác biệt ở đây là SQL được thực thi SELECT * FROM Products WHERE Cost >= 25
.
Từ POV của bạn là một nhà phát triển, điều này trông giống nhau. Tuy nhiên, từ quan điểm hiệu suất, bạn chỉ có thể trả lại 2 bản ghi trên mạng thay vì 20.000 ....
IQueryable<Product>
- sẽ dành riêng cho ORM hoặc kho lưu trữ của bạn, v.v.
foreach
hoặc gọi ToList()
), bạn không thực sự nhấn DB.
Về bản chất, công việc của nó rất giống với IEnumerable<T>
- đại diện cho một nguồn dữ liệu Queryable
có thể truy vấn - sự khác biệt là các phương thức LINQ khác nhau (bật ) có thể cụ thể hơn, để xây dựng truy vấn bằng cách sử dụng Expression
cây thay vì đại biểu (đó là cách Enumerable
sử dụng).
Các cây biểu thức có thể được kiểm tra bởi nhà cung cấp LINQ đã chọn của bạn và biến thành một truy vấn thực tế - mặc dù bản thân đó là một nghệ thuật đen.
Điều này thực sự không phù hợp ElementType
, Expression
và Provider
- nhưng trong thực tế, bạn hiếm khi cần quan tâm đến điều này như một người dùng . Chỉ người thực hiện LINQ cần biết các chi tiết chính.
Nhận xét lại; Tôi không chắc chắn những gì bạn muốn bằng ví dụ, nhưng hãy xem xét LINQ-to-SQL; đối tượng trung tâm ở đây là một DataContext
, đại diện cho trình bao bọc cơ sở dữ liệu của chúng tôi. Điều này thường có một thuộc tính trên mỗi bảng (ví dụ Customers
:) và một bảng thực hiện IQueryable<Customer>
. Nhưng chúng ta không sử dụng nó trực tiếp; xem xét:
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);
}
}
điều này trở thành (bởi trình biên dịch C #):
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
một lần nữa được giải thích (bởi trình biên dịch C #) là:
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
Điều quan trọng là, các phương thức tĩnh trên các Queryable
cây biểu thức, mà - chứ không phải IL thông thường, được biên dịch thành một mô hình đối tượng. Ví dụ: chỉ cần nhìn vào "Ở đâu", điều này cho chúng ta một cái gì đó tương đương với:
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) ...
Không phải trình biên dịch đã làm rất nhiều cho chúng ta? Mô hình đối tượng này có thể được xé ra, kiểm tra ý nghĩa của nó và đặt lại với nhau bởi trình tạo TSQL - đưa ra một cái gì đó như:
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
(chuỗi có thể kết thúc như một tham số; tôi không thể nhớ)
Không ai có thể làm điều này nếu chúng ta vừa sử dụng một đại biểu. Và đây là điểm của Queryable
/ IQueryable<T>
: nó cung cấp điểm vào để sử dụng cây biểu thức.
Tất cả điều này là rất phức tạp, vì vậy đó là một công việc tốt mà trình biên dịch làm cho nó tốt đẹp và dễ dàng cho chúng tôi.
Để biết thêm thông tin, hãy xem " C # in Depth " hoặc " LINQ in Action ", cả hai đều cung cấp bảo hiểm cho các chủ đề này.
Mặc dù Reed Copsey và Marc Gravell đã mô tả về IQueryable
(và cũng IEnumerable
) đủ, nhưng tôi muốn thêm một chút nữa ở đây bằng cách cung cấp một ví dụ nhỏ trên IQueryable
và IEnumerable
như nhiều người dùng đã yêu cầu
Ví dụ : Tôi đã tạo hai bảng trong cơ sở dữ liệu
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)
Khóa chính ( PersonId
) của bảng Employee
cũng là khóa forgein ( personid
) của bảngPerson
Tiếp theo tôi đã thêm mô hình thực thể ado.net trong ứng dụng của mình và tạo bên dưới lớp dịch vụ trên đó
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;
}
}
chúng chứa cùng linq. Nó được gọi program.cs
như được định nghĩa dưới đây
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();
}
}
Đầu ra là như nhau cho cả hai rõ ràng
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
Vậy câu hỏi là sự khác biệt / ở đâu? Nó dường như không có sự khác biệt nào phải không? Có thật không!!
Chúng ta hãy xem các truy vấn sql được tạo và thực hiện bởi framwork 5 thực thể trong khoảng thời gian này
Phần thực thi 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]
Phần thực thi vô số
--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)
Kịch bản chung cho cả hai phần thực thi
/* 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
*/
Vì vậy, bây giờ bạn có một vài câu hỏi, hãy để tôi đoán chúng và cố gắng trả lời chúng
Tại sao các tập lệnh khác nhau được tạo ra cho cùng một kết quả?
Hãy tìm hiểu một số điểm ở đây,
tất cả các truy vấn có một phần chung
WHERE [Extent1].[PersonId] IN (0,1,2,3)
tại sao? Bởi vì cả hai chức năng IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
và
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
các SomeServiceClass
chứa một dòng phổ biến trong các truy vấn LINQ
where employeesToCollect.Contains(e.PersonId)
Hơn tại sao
AND (N'M' = [Extent1].[Gender])
phần bị thiếu trong IEnumerable
phần thực thi, trong khi trong cả hai chức năng gọi, chúng tôi đã sử dụng Where(i => i.Gender == "M") in
chương trình.cs`
Bây giờ chúng ta đang ở điểm khác biệt giữa
IQueryable
vàIEnumerable
Framwork thực thể làm gì khi một IQueryable
phương thức được gọi, nó lấy câu lệnh linq được viết bên trong phương thức và cố gắng tìm hiểu xem có nhiều biểu thức linq được xác định trên tập kết quả hay không, sau đó tập hợp tất cả các truy vấn linq được xác định cho đến khi kết quả cần tìm nạp và xây dựng sql phù hợp hơn truy vấn để thực hiện.
Nó cung cấp rất nhiều lợi ích như,
như ở đây trong ví dụ máy chủ sql chỉ trả về ứng dụng hai hàng sau khi thực thi IQueryable` nhưng trả về BA hàng cho truy vấn IEnumerable tại sao?
Trong trường hợp IEnumerable
phương thức, khung thực thể lấy câu lệnh linq được viết bên trong phương thức và xây dựng truy vấn sql khi kết quả cần tìm nạp. nó không bao gồm phần linq còn lại để xây dựng truy vấn sql. Giống như ở đây không lọc được thực hiện trong máy chủ sql trên cột gender
.
Nhưng đầu ra có giống nhau không? Bởi vì 'IEnumerable lọc kết quả hơn nữa ở cấp ứng dụng sau khi truy xuất kết quả từ máy chủ sql
Vậy, ai đó nên chọn cái gì? Cá nhân tôi thích xác định kết quả hàm IQueryable<T>
vì có rất nhiều lợi ích mà nó mang lại IEnumerable
, bạn có thể tham gia hai hoặc nhiều hàm IQueryable, tạo ra tập lệnh cụ thể hơn cho máy chủ sql.
Ở đây trong ví dụ, bạn có thể thấy một IQueryable Query(IQueryableQuery2)
tập lệnh tạo ra một kịch bản cụ thể hơn so với IEnumerable query(IEnumerableQuery2)
quan điểm của tôi.
Nó cho phép truy vấn sâu hơn nữa xuống dòng. Nếu điều này vượt ra ngoài ranh giới dịch vụ, thì người dùng của đối tượng IQueryable này sẽ được phép làm nhiều hơn với nó.
Chẳng hạn, nếu bạn đang sử dụng tải lười biếng với nhibernate thì điều này có thể dẫn đến biểu đồ được tải khi / nếu cần.