Pertanyaan Mengembalikan IEnumerable vs. IQueryable


Apa perbedaan antara kembali IQueryable<T> vs. IEnumerable<T>?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

Apakah keduanya akan ditunda eksekusi dan kapan harus lebih disukai daripada yang lain?


898
2018-05-20 18:13


asal


Jawaban:


Ya, keduanya akan memberi Anda eksekusi ditangguhkan.

Perbedaannya adalah itu IQueryable<T> adalah antarmuka yang memungkinkan LINQ-to-SQL (LINQ.-to-anything really) berfungsi. Jadi jika Anda lebih menyaring kueri Anda di IQueryable<T>, query itu akan dieksekusi dalam database, jika memungkinkan.

Untuk IEnumerable<T> kasus, itu akan menjadi LINQ-to-object, yang berarti bahwa semua objek yang cocok dengan permintaan asli harus dimuat ke dalam memori dari database.

Dalam kode:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Kode itu akan mengeksekusi SQL hanya untuk memilih pelanggan emas. Kode berikut, di sisi lain, akan mengeksekusi kueri asli dalam database, lalu memfilter pelanggan non-emas di memori:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Ini adalah perbedaan yang cukup penting, dan sedang dikerjakan IQueryable<T> dalam banyak kasus dapat menyelamatkan Anda dari mengembalikan terlalu banyak baris dari database. Contoh utama lainnya adalah melakukan paging: Jika Anda menggunakan Take dan Skip di IQueryable, Anda hanya akan mendapatkan jumlah baris yang diminta; melakukan itu pada IEnumerable<T> akan menyebabkan semua baris Anda dimuat dalam memori.


1522
2018-05-20 18:19



Jawaban atas bagus tetapi tidak menyebutkan pohon ekspresi yang menjelaskan "bagaimana" kedua antarmuka berbeda. Pada dasarnya, ada dua set ekstensi LINQ yang identik. Where(), Sum(), Count(), FirstOrDefault(), dll semuanya memiliki dua versi: satu yang menerima fungsi dan satu yang menerima ekspresi.

  • Itu IEnumerable tanda tangan versi adalah: Where(Func<Customer, bool> predicate)

  • Itu IQueryable tanda tangan versi adalah: Where(Expression<Func<Customer, bool>> predicate)

Anda mungkin telah menggunakan keduanya tanpa menyadarinya karena keduanya disebut menggunakan sintaks identik:

misalnya Where(x => x.City == "<City>") bekerja pada keduanya IEnumerable dan IQueryable

  • Ketika menggunakan Where() pada IEnumerable koleksi, kompilator melewati fungsi yang dikompilasi Where()

  • Ketika menggunakan Where() pada IQueryable koleksi, kompilator melewati pohon ekspresi ke Where(). Pohon ekspresi seperti sistem pantulan tetapi untuk kode. Compiler mengubah kode Anda menjadi struktur data yang menggambarkan apa yang dilakukan kode Anda dalam format yang mudah dicerna.

Kenapa repot-repot dengan pohon ekspresi ini? saya hanya ingin Where() untuk memfilter data saya. Alasan utamanya adalah bahwa baik ORM EF maupun Linq2SQL dapat mengkonversi pohon ekspresi langsung ke dalam SQL di mana kode Anda akan berjalan lebih cepat.

Oh, itu terdengar seperti peningkatan kinerja gratis, yang harus saya gunakan AsQueryable() di semua tempat dalam kasus itu?  Tidak, IQueryable hanya berguna jika penyedia data yang mendasarinya dapat melakukan sesuatu dengannya. Mengubah sesuatu seperti biasa List untuk IQueryable tidak akan memberi Anda manfaat apa pun.


195
2018-02-14 08:00



Ya, keduanya menggunakan eksekusi yang ditangguhkan. Mari kita ilustrasikan perbedaan menggunakan profiler SQL Server ....

Saat kami menjalankan kode berikut:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

Dalam profiler SQL Server kami menemukan perintah yang sama dengan:

"SELECT * FROM [dbo].[WebLog]"

Diperlukan waktu sekitar 90 detik untuk menjalankan blok kode itu terhadap tabel WebLog yang memiliki 1 juta catatan.

Jadi, semua catatan tabel dimuat ke dalam memori sebagai objek, dan kemudian dengan masing-masing. Dimana () itu akan menjadi filter lain dalam memori terhadap objek-objek ini.

Saat kami gunakan IQueryable dari pada IEnumerable pada contoh di atas (baris kedua):

Dalam profiler SQL Server kami menemukan perintah yang sama dengan:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

Diperlukan waktu sekitar empat detik untuk menjalankan blok kode ini IQueryable.

IQueryable memiliki properti yang disebut Expression yang menyimpan ekspresi pohon yang mulai dibuat ketika kami menggunakan result dalam contoh kita (yang disebut eksekusi yang ditangguhkan), dan pada akhirnya ekspresi ini akan diubah menjadi query SQL untuk dijalankan pada mesin database.


56
2018-06-08 01:11



Keduanya akan memberi Anda eksekusi yang ditangguhkan, ya.

Adapun yang lebih disukai daripada yang lain, itu tergantung pada apa sumber data yang mendasari Anda.

Mengembalikan sebuah IEnumerable secara otomatis akan memaksa runtime untuk menggunakan LINQ ke Objects untuk mengkueri koleksi Anda.

Mengembalikan IQueryable (yang mengimplementasikan IEnumerable, by the way) menyediakan fungsionalitas tambahan untuk menerjemahkan permintaan Anda menjadi sesuatu yang mungkin berkinerja lebih baik pada sumber yang mendasarinya (LINQ to SQL, LINQ to XML, dll.).


54
2018-05-20 18:18



Secara umum Anda ingin mempertahankan jenis statis asli dari kueri sampai itu penting.

Untuk alasan ini, Anda dapat mendefinisikan variabel Anda sebagai 'var', bukan keduanya IQueryable<> atau IEnumerable<> dan Anda akan tahu bahwa Anda tidak mengubah jenisnya.

Jika Anda memulai dengan IQueryable<>, Anda biasanya ingin menyimpannya sebagai IQueryable<> sampai ada beberapa alasan kuat untuk mengubahnya. Alasan untuk ini adalah bahwa Anda ingin memberikan prosesor query informasi sebanyak mungkin. Misalnya, jika Anda hanya akan menggunakan 10 hasil (Anda sudah menelepon Take(10)) maka Anda ingin SQL Server tahu tentang itu sehingga dapat mengoptimalkan rencana kueri dan mengirim Anda hanya data yang akan Anda gunakan.

Alasan kuat untuk mengubah jenisnya IQueryable<> untuk IEnumerable<> Mungkin Anda memanggil beberapa fungsi ekstensi yang pelaksanaan IQueryable<> dalam objek tertentu Anda tidak dapat menangani atau menangani secara tidak efisien. Dalam hal ini, Anda mungkin ingin mengonversi jenis menjadi IEnumerable<> (dengan menugaskan ke variabel tipe IEnumerable<> atau dengan menggunakan AsEnumerable metode ekstensi misalnya) sehingga fungsi ekstensi yang Anda panggil akhirnya menjadi yang ada di Enumerable kelas bukan Queryable kelas.


23
2018-05-15 00:00



Secara umum saya akan merekomendasikan hal berikut:

Untuk mengembalikan IQueryable <T> jika Anda ingin mengaktifkan pengembang menggunakan metode Anda untuk memperbaiki kueri yang Anda kembalikan sebelum mengeksekusi.

Jika Anda ingin mengangkut hanya satu set Objek untuk menghitung lebih, cukup ambil IEnumerable.

Bayangkan sebuah IQueryable seperti apa itu, sebuah "permintaan" untuk data (yang dapat Anda sempurnakan jika Anda mau)

Sebuah IEnumerable adalah sekumpulan objek (yang telah diterima atau dibuat) di mana Anda dapat menghitung.


21
2018-04-11 13:09



Ada posting blog dengan sampel kode sumber singkat tentang bagaimana penyalahgunaan IEnumerable<T> dapat secara dramatis memengaruhi performa query LINQ: Kerangka Entitas: IQueryable vs. IEnumerable.

Jika kita menggali lebih dalam dan melihat sumbernya, kita dapat melihat bahwa ada metode penyuluhan yang jelas berbeda yang dilakukan IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

dan IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

Yang pertama mengembalikan iterator enumerable, dan yang kedua membuat query melalui penyedia query, yang ditentukan dalam IQueryable sumber.


17
2018-04-23 22:45