Apa gunanya IQueryable
dalam konteks LINQ?
Apakah digunakan untuk mengembangkan metode penyuluhan atau tujuan lain?
Apa gunanya IQueryable
dalam konteks LINQ?
Apakah digunakan untuk mengembangkan metode penyuluhan atau tujuan lain?
Jawaban:
Jawaban Marc Gravell sangat lengkap, tetapi saya pikir saya akan menambahkan sesuatu tentang ini dari sudut pandang pengguna, juga ...
Perbedaan utama, dari perspektif pengguna, adalah bahwa, ketika Anda menggunakan IQueryable<T>
(dengan penyedia yang mendukung hal-hal dengan benar), Anda dapat menghemat banyak sumber daya.
Misalnya, jika Anda bekerja melawan basis data jauh, dengan banyak sistem ORM, Anda memiliki opsi untuk mengambil data dari tabel dengan dua cara, yang mengembalikan IEnumerable<T>
, dan yang mengembalikan IQueryable<T>
. Misalnya, Anda memiliki tabel Produk, dan Anda ingin mendapatkan semua produk yang biayanya> $ 25.
Jika kamu melakukan:
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Apa yang terjadi di sini, adalah database memuat semua produk, dan meneruskannya ke program Anda. Program Anda kemudian menyaring data. Intinya, database melakukan SELECT * FROM Products
, dan mengembalikan setiap produk kepada Anda.
Dengan IQueryable<T>
penyedia yang tepat , di sisi lain, Anda dapat melakukan:
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
Kode tersebut terlihat sama, tetapi perbedaannya di sini adalah SQL yang dieksekusi SELECT * FROM Products WHERE Cost >= 25
.
Dari POV Anda sebagai pengembang, ini terlihat sama. Namun, dari sudut pandang kinerja, Anda hanya dapat mengembalikan 2 catatan di seluruh jaringan, bukan 20.000 ....
IQueryable<Product>
- akan spesifik untuk ORM atau repositori Anda, dll.
foreach
, atau panggilan ToList()
), Anda tidak benar-benar menekan DB.
Pada dasarnya pekerjaannya sangat mirip dengan IEnumerable<T>
- untuk mewakili sumber data yang dapat ditanyakan - perbedaannya adalah bahwa berbagai metode LINQ (on Queryable
) dapat lebih spesifik, untuk membangun kueri menggunakan Expression
pohon daripada delegasi (yang digunakan adalah apa Enumerable
).
Pohon ekspresi dapat diperiksa oleh penyedia LINQ yang Anda pilih dan berubah menjadi permintaan yang sebenarnya - meskipun itu sendiri merupakan seni hitam.
Ini benar-benar ke bawah ElementType
, Expression
dan Provider
- tetapi dalam kenyataannya Anda jarang perlu peduli tentang ini sebagai pengguna . Hanya pelaksana LINQ yang perlu mengetahui detail berdarah.
Komentar ulang; Saya tidak yakin apa yang Anda inginkan dengan contoh, tetapi pertimbangkan LINQ-to-SQL; objek utama di sini adalah DataContext
, yang mewakili pembungkus basis data kami. Ini biasanya memiliki properti per tabel (misalnya, Customers
), dan tabel mengimplementasikan IQueryable<Customer>
. Tapi kami tidak menggunakan itu secara langsung; mempertimbangkan:
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);
}
}
ini menjadi (oleh kompiler C #):
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
yang lagi ditafsirkan (oleh kompiler C #) sebagai:
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
Yang penting, metode statis pada Queryable
pohon ekspresi mengambil, yang - daripada IL biasa, bisa dikompilasi ke model objek. Misalnya - hanya melihat "Di mana", ini memberi kita sesuatu yang sebanding dengan:
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) ...
Bukankah kompiler melakukan banyak hal untuk kita? Model objek ini dapat dirobek, diperiksa apa artinya, dan disatukan kembali oleh generator TSQL - memberikan sesuatu seperti:
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
(string mungkin berakhir sebagai parameter; Saya tidak ingat)
Semua ini tidak akan mungkin terjadi jika kami baru saja menggunakan delegasi. Dan ini adalah titik Queryable
/ IQueryable<T>
: ia menyediakan titik masuk untuk menggunakan pohon ekspresi.
Semua ini sangat kompleks, sehingga merupakan pekerjaan yang bagus sehingga kompiler membuatnya bagus dan mudah bagi kami.
Untuk informasi lebih lanjut, lihat " C # in Depth " atau " LINQ in Action ", yang keduanya menyediakan cakupan topik-topik ini.
Meskipun Reed Copsey dan Marc Gravell sudah menjelaskan tentang IQueryable
(dan juga IEnumerable
) cukup, saya ingin menambahkan lebih banyak di sini dengan memberikan contoh kecil IQueryable
dan IEnumerable
karena banyak pengguna memintanya
Contoh : Saya telah membuat dua tabel dalam database
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)
Kunci Utama ( PersonId
) dari tabel Employee
juga merupakan kunci forgein ( personid
) dari tabelPerson
Selanjutnya saya menambahkan model entitas ado.net dalam aplikasi saya dan membuat kelas layanan di bawah itu
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;
}
}
mereka berisi LINQ yang sama. Itu disebut program.cs
seperti yang didefinisikan di bawah ini
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();
}
}
Outputnya sama untuk keduanya
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
Jadi pertanyaannya adalah apa / di mana bedanya? Sepertinya tidak ada bedanya kan? Betulkah!!
Mari kita lihat query sql yang dihasilkan dan dieksekusi oleh entitas framwork 5 selama periode ini
Bagian eksekusi 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]
Bagian eksekusi 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)
Skrip umum untuk kedua bagian eksekusi
/* 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
*/
Jadi, Anda memiliki beberapa pertanyaan sekarang, izinkan saya menebaknya dan mencoba menjawabnya
Mengapa skrip yang berbeda dihasilkan untuk hasil yang sama?
Mari kita cari beberapa poin di sini,
semua pertanyaan memiliki satu bagian yang sama
WHERE [Extent1].[PersonId] IN (0,1,2,3)
Mengapa? Karena kedua fungsi IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
dan
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
dari SomeServiceClass
berisi satu baris umum dalam permintaan LINQ
where employeesToCollect.Contains(e.PersonId)
Daripada mengapa
AND (N'M' = [Extent1].[Gender])
bagian itu hilang di IEnumerable
bagian eksekusi, sementara di kedua fungsi memanggil kami menggunakan Where(i => i.Gender == "M") in
program.cs`
Sekarang kita berada di titik di mana perbedaan terjadi antara
IQueryable
danIEnumerable
Apa yang dilakukan kerangka entitas ketika suatu IQueryable
metode dipanggil, ia mengambil pernyataan linq yang ditulis di dalam metode dan mencoba untuk mencari tahu apakah lebih banyak ekspresi linq didefinisikan pada resultset, itu kemudian mengumpulkan semua permintaan linq didefinisikan sampai hasilnya perlu mengambil dan membangun sql yang lebih tepat permintaan untuk dieksekusi.
Ini memberikan banyak manfaat seperti,
seperti di sini dalam contoh sql server kembali ke aplikasi hanya dua baris setelah eksekusi IQueryable` tetapi mengembalikan TIGA baris untuk permintaan IEnumerable mengapa?
Dalam hal IEnumerable
metode, kerangka kerja mengambil pernyataan LINQ yang ditulis di dalam metode dan membangun kueri sql ketika hasilnya perlu diambil. itu tidak termasuk bagian rest linq untuk membangun kueri sql. Seperti di sini, tidak ada penyaringan yang dilakukan di sql server pada kolom gender
.
Tetapi hasilnya sama? Karena 'IEnumerable memfilter hasil lebih lanjut di level aplikasi setelah mengambil hasil dari sql server
JADI, apa yang harus seseorang pilih? Saya pribadi lebih suka mendefinisikan hasil fungsi IQueryable<T>
karena ada banyak manfaatnya IEnumerable
seperti, Anda bisa bergabung dengan dua atau lebih fungsi IQueryable, yang menghasilkan skrip yang lebih spesifik ke server sql.
Di sini, dalam contoh Anda dapat melihat IQueryable Query(IQueryableQuery2)
skrip menghasilkan lebih spesifik daripada IEnumerable query(IEnumerableQuery2)
yang jauh lebih dapat diterima menurut saya.
Hal ini memungkinkan untuk pertanyaan lebih lanjut di telepon. Jika ini melampaui batas layanan, maka pengguna objek yang IQueryable ini akan diizinkan untuk berbuat lebih banyak dengannya.
Misalnya jika Anda menggunakan lazy loading dengan nhibernate, ini dapat mengakibatkan grafik dimuat ketika / jika diperlukan.