Mengembalikan IEnumerable <T> vs IQueryable <T>

1085

Apa perbedaan antara kembali IQueryable<T>vs IEnumerable<T>, kapan yang satu lebih disukai daripada yang lain?

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;
stackoverflowuser
sumber

Jawaban:

1778

Ya, keduanya akan memberi Anda eksekusi yang ditangguhkan .

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

Untuk IEnumerable<T>kasus ini, 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 menjalankan SQL hanya untuk memilih pelanggan emas. Kode berikut, di sisi lain, akan mengeksekusi permintaan asli dalam database, kemudian menyaring pelanggan non-emas di memori:

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

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

driis
sumber
32
Penjelasan yang bagus. Apakah ada situasi di mana IEnumerable lebih disukai daripada IQueryable?
fjxx
8
Jadi kita dapat mengatakan bahwa jika kita menggunakan IQueryable untuk meminta Memory Obyek, maka mereka tidak akan ada perbedaan antara IEnumerable dan IQueryable?
Tarik
11
PERINGATAN: Meskipun IQueryable mungkin merupakan solusi yang menggoda karena optimasi yang disebutkan, IQueryable tidak boleh melewati masa repositori atau lapisan layanan. Ini untuk melindungi database Anda dari overhead yang disebabkan oleh "menumpuk ekspresi LINQ".
Yorro
48
@ fjxx Ya. Jika Anda ingin pemfilteran berulang pada hasil asli Anda (beberapa hasil akhir). Melakukan hal itu pada antarmuka IQueryable akan membuat beberapa bolak-balik ke database, di mana seperti melakukannya di IEnumerable akan melakukan penyaringan dalam memori, membuatnya lebih cepat (kecuali jumlah data BESAR)
Per Hornshøj-Schierbeck
34
Alasan lain untuk memilih IEnumerableuntuk IQueryableadalah bahwa tidak semua operasi LINQ didukung oleh semua penyedia LINQ. Jadi, selama Anda tahu apa yang Anda lakukan, Anda bisa menggunakan IQueryableuntuk mendorong sebanyak mungkin permintaan ke penyedia LINQ (LINQ2SQL, EF, NHibernate, MongoDB, dll.). Tetapi jika Anda membiarkan kode lain melakukan apa pun yang diinginkan dengan Anda, IQueryableAnda pada akhirnya akan mendapat masalah karena beberapa kode klien di suatu tempat menggunakan operasi yang tidak didukung. Saya setuju dengan rekomendasi untuk tidak merilis IQueryable"into the wild" melewati repositori atau lapisan yang setara.
Avish
302

Jawaban teratas adalah baik tetapi tidak menyebutkan pohon ekspresi yang menjelaskan "bagaimana" dua antarmuka berbeda. Pada dasarnya, ada dua set ekstensi LINQ yang identik. Where(), Sum(), Count(), FirstOrDefault(), Dll semua memiliki dua versi: satu yang menerima fungsi dan satu yang menerima ekspresi.

  • Tanda IEnumerabletangan versi adalah:Where(Func<Customer, bool> predicate)

  • Tanda IQueryabletangan 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 IEnumerabledanIQueryable

  • Saat menggunakan Where()pada IEnumerablekoleksi, kompiler meneruskan fungsi yang dikompilasi keWhere()

  • Saat menggunakan Where()pada IQueryablekoleksi, kompiler meneruskan pohon ekspresi ke Where(). Pohon ekspresi seperti sistem refleksi tetapi untuk kode. Kompiler mengubah kode Anda menjadi struktur data yang menjelaskan apa yang kode Anda lakukan dalam format yang mudah dicerna.

Kenapa repot-repot dengan pohon ekspresi ini? Saya hanya ingin Where()memfilter data saya. Alasan utama adalah bahwa kedua ORM EF dan Linq2SQL dapat mengkonversi pohon ekspresi langsung ke SQL di mana kode Anda akan mengeksekusi lebih cepat.

Oh, itu terdengar seperti peningkatan kinerja gratis, haruskah saya menggunakan AsQueryable()semua tempat dalam kasus itu? Tidak, IQueryablehanya berguna jika penyedia data yang mendasarinya dapat melakukan sesuatu dengannya. Mengubah sesuatu seperti biasa Listmenjadi IQueryabletidak akan memberi Anda manfaat apa pun.

Yakub
sumber
9
IMO lebih baik daripada jawaban yang diterima. Namun, saya tidak mendapatkan satu hal: IQueryable tidak memberikan manfaat apa pun untuk objek biasa, OK, tapi apakah ini lebih buruk? Karena jika itu tidak memberikan manfaat, itu tidak cukup alasan untuk memilih IEnumerable, sehingga gagasan menggunakan IQueryable di semua tempat tetap valid.
Sergei Tachenov
1
Sergey, IQueryable memperluas IEnumerable sehingga ketika menggunakan IQueryable Anda memuat lebih banyak ke dalam memori daripada instantiasi IEnumerable! Jadi inilah argumennya. ( stackoverflow.com/questions/12064828/... c ++ meskipun saya pikir saya bisa memperkirakan ini)
Viking
Setuju dengan Sergei tentang ini menjadi jawaban terbaik (meskipun jawaban yang diterima baik-baik saja). Saya akan menambahkan bahwa dalam pengalaman saya, IQueryabletidak fungsi parse serta IEnumerabletidak: misalnya, jika Anda ingin tahu mana unsur-unsur dari DBSet<BookEntity>tidak dalam List<BookObject>, dbSetObject.Where(e => !listObject.Any(o => o.bookEntitySource == e))melempar pengecualian: Expression of type 'BookEntity' cannot be used for parameter of type 'BookObject' of method 'Boolean Contains[BookObject] (IEnumerable[BookObject], BookObject)'. Saya harus menambahkan .ToList()setelah dbSetObject.
Jean-David Lanz
80

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

Ketika 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 SQL Server profiler kami menemukan perintah 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. Di mana () itu akan menjadi filter lain dalam memori terhadap objek-objek ini.

Ketika kita menggunakan IQueryablebukannya IEnumerabledalam contoh di atas (baris kedua):

Dalam SQL Server profiler kami menemukan perintah sama dengan:

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

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

IQueryable memiliki properti yang disebut Expressionyang menyimpan ekspresi pohon yang mulai dibuat ketika kami menggunakan resultdalam contoh kami (yang disebut eksekusi ditangguhkan), dan pada akhirnya ekspresi ini akan dikonversi ke query SQL untuk dijalankan pada mesin database.

Kasper Roma
sumber
5
Ini mengajarkan saya ketika melakukan casting ke IEnumerable, IQueryable yang mendasarinya kehilangan metode ekstensi IQueryable.
Yiping
56

Keduanya akan memberi Anda eksekusi yang ditangguhkan, ya.

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

Mengembalikan sebuah IEnumerableakan secara otomatis memaksa runtime untuk menggunakan LINQ ke Objek untuk menanyakan koleksi Anda.

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

Justin Niessner
sumber
30

Secara umum saya akan merekomendasikan yang berikut ini:

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

  • Kembali IEnumerablejika Anda ingin memindahkan satu set Objek untuk disebutkan.

Bayangkan IQueryablesebagai apa itu - "permintaan" untuk data (yang dapat Anda saring jika Anda mau). An IEnumerableadalah seperangkat objek (yang telah diterima atau telah dibuat) yang dapat Anda hitung.

sebastianmehler
sumber
2
"Dapat menghitung," bukan "dapat menghitung IE."
Casey
28

Banyak yang telah dikatakan sebelumnya, tetapi kembali ke akarnya, dengan cara yang lebih teknis:

  1. IEnumerable adalah kumpulan objek dalam memori yang dapat Anda hitung - urutan dalam memori yang memungkinkan untuk foreachdiulang (membuatnya mudah untuk dilakukan dalam loop, meskipun Anda hanya dapat menggunakannya IEnumerator). Mereka tinggal di memori apa adanya.
  2. IQueryable adalah pohon ekspresi yang akan diterjemahkan ke sesuatu yang lain di beberapa titik dengan kemampuan untuk menghitung hasil akhir . Saya kira inilah yang membingungkan kebanyakan orang.

Mereka jelas memiliki konotasi yang berbeda.

IQueryablemewakili pohon ekspresi (permintaan, sederhana) yang akan diterjemahkan ke sesuatu yang lain oleh penyedia permintaan yang mendasari segera setelah rilis API disebut, seperti fungsi agregat LINQ (Jumlah, Hitungan, dll.) atau Daftar [Array, Kamus,. ..] Dan IQueryableobjek juga diimplementasikan IEnumerable, IEnumerable<T>sehingga jika mereka merepresentasikan kueri , hasil dari kueri itu bisa diulang. Itu berarti IQueryable tidak harus menjadi pertanyaan saja. Istilah yang tepat adalah mereka pohon ekspresi .

Sekarang bagaimana ekspresi itu dieksekusi dan apa yang mereka berubah adalah semua yang disebut penyedia kueri (eksekutor ekspresi kita dapat memikirkannya).

Di dunia Entity Framework (yaitu penyedia sumber data yang mendasari mistis, atau penyedia kueri) IQueryablediterjemahkan ke dalam kueri T-SQL asli . Nhibernatemelakukan hal serupa dengan mereka. Anda dapat menulis sendiri mengikuti konsep yang dijelaskan dengan baik di LINQ: Membangun tautan Penyedia IQueryable , misalnya, dan Anda mungkin ingin memiliki API kueri khusus untuk layanan penyedia toko produk Anda.

Jadi pada dasarnya, IQueryableobjek sedang dibangun jauh sampai kita secara eksplisit melepaskannya dan memberitahu sistem untuk menulis ulang mereka ke dalam SQL atau apa pun dan mengirimkan rantai eksekusi untuk diproses selanjutnya.

Seolah-olah untuk menunda eksekusi, ini adalah LINQfitur untuk menahan skema pohon ekspresi di memori dan mengirimkannya ke eksekusi hanya berdasarkan permintaan, setiap kali API tertentu dipanggil terhadap urutan (Hitungan yang sama, Daftar, dll.).

Penggunaan keduanya sangat tergantung pada tugas yang Anda hadapi untuk kasus tertentu. Untuk pola repositori terkenal saya pribadi memilih untuk kembali IList, yaitu IEnumerablelebih dari Daftar (pengindeks dan sejenisnya). Jadi ini adalah saran saya untuk menggunakan IQueryablehanya di dalam repositori dan IEnumerable di tempat lain dalam kode. Tidak mengatakan tentang kekhawatiran yang dapat diuji yang IQueryablerusak dan merusak prinsip pemisahan kekhawatiran . Jika Anda mengembalikan ekspresi dari dalam repositori, konsumen dapat bermain dengan lapisan kegigihan seperti yang mereka inginkan.

Sedikit tambahan ke kekacauan :) (dari diskusi di komentar)) Tidak satu pun dari mereka adalah objek dalam memori karena mereka bukan tipe nyata per se, mereka adalah penanda jenis - jika Anda ingin pergi sedalam itu. Tetapi masuk akal (dan itulah sebabnya bahkan MSDN mengatakannya seperti ini) untuk memikirkan IEnumerables sebagai koleksi dalam memori sedangkan IQueryables sebagai pohon ekspresi. Intinya adalah bahwa antarmuka IQueryable mewarisi antarmuka IEnumerable sehingga jika itu merupakan permintaan, hasil dari permintaan itu dapat disebutkan. Pencacahan menyebabkan pohon ekspresi yang terkait dengan objek IQueryable dieksekusi. Jadi, sebenarnya, Anda tidak dapat benar-benar memanggil anggota IEnumerable tanpa memiliki objek di memori. Ini akan masuk ke sana jika Anda melakukannya, jika tidak kosong. IQueryables hanyalah pertanyaan, bukan data.

Arman McHitarian
sumber
3
Komentar bahwa IEnumerables selalu di memori tidak selalu benar. Antarmuka IQueryable mengimplementasikan antarmuka IEnumerable. Karena itu, Anda bisa meneruskan IQueryable mentah yang mewakili kueri LINQ-to-SQL ke tampilan yang mengharapkan IEnumerable! Anda mungkin terkejut menemukan bahwa konteks data Anda telah kedaluwarsa atau Anda akhirnya mengalami masalah dengan MARS (beberapa set hasil aktif).
jadi, pada kenyataannya, Anda tidak dapat benar-benar memanggil anggota IEnumerable tanpa memiliki objek di memori. Ini akan masuk ke sana jika Anda melakukannya, jika tidak kosong. IQueryables hanyalah pertanyaan, bukan data. Tapi saya benar-benar mengerti maksud Anda. Saya akan menambahkan komentar tentang ini.
Arman McHitarian
@AlexanderPritchard tidak ada yang objek dalam memori karena mereka bukan tipe nyata per se, mereka adalah penanda tipe - jika Anda ingin masuk sedalam itu. Tetapi masuk akal (dan itulah sebabnya bahkan MSDN mengatakannya seperti ini) untuk memikirkan IEnumerables sebagai koleksi dalam memori sedangkan IQueryables sebagai pohon ekspresi. Intinya adalah bahwa antarmuka IQueryable mewarisi antarmuka IEnumerable sehingga jika itu merupakan permintaan, hasil dari permintaan itu dapat disebutkan. Pencacahan menyebabkan pohon ekspresi yang terkait dengan objek IQueryable dieksekusi.
Arman McHitarian
24

Secara umum, Anda ingin mempertahankan tipe statis asli kueri sampai masalah.

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

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

Alasan kuat untuk mengubah jenis dari IQueryable<>menjadi IEnumerable<>mungkin karena Anda memanggil beberapa fungsi ekstensi yang implementasi IQueryable<>di objek khusus Anda tidak dapat menangani atau menangani secara tidak efisien. Dalam hal itu, Anda mungkin ingin mengonversi tipe ke IEnumerable<>(dengan menetapkan ke variabel tipe IEnumerable<>atau dengan menggunakan AsEnumerablemetode ekstensi misalnya) sehingga fungsi ekstensi yang Anda panggil akhirnya menjadi yang ada di Enumerablekelas alih-alih Queryablekelas.

AJS
sumber
18

Ada posting blog dengan contoh kode sumber singkat tentang bagaimana penyalahgunaan IEnumerable<T>secara dramatis dapat mempengaruhi kinerja permintaan LINQ: Kerangka Entitas: IQueryable vs IEnumerable .

Jika kita menggali lebih dalam dan mencari sumbernya, kita dapat melihat bahwa ada beberapa metode ekstensi yang berbeda untuk 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 yang dapat dihitung, dan yang kedua membuat kueri melalui penyedia kueri, ditentukan dalam IQueryablesumber.

Olexander Ivanitskyi
sumber
11

Saya baru-baru ini mengalami masalah dengan IEnumerablev IQueryable. Algoritme yang digunakan pertama kali melakukan IQueryablekueri untuk mendapatkan serangkaian hasil. Ini kemudian diteruskan ke foreachloop, dengan item yang dipakai sebagai kelas Entity Framework (EF). Kelas EF ini kemudian digunakan dalam fromklausa kueri Linq to Entity, menyebabkan hasilnya IEnumerable.

Saya cukup baru di EF dan Linq untuk Entitas, jadi butuh beberapa saat untuk mencari tahu apa hambatannya. Dengan menggunakan MiniProfiling, saya menemukan kueri dan kemudian mengonversi semua operasi individual menjadi satu IQueryableLinq untuk kueri Entitas. The IEnumerablemengambil 15 detik dan IQueryablemengambil 0,5 detik untuk mengeksekusi. Ada tiga tabel yang terlibat dan, setelah membaca ini, saya percaya bahwa IEnumerablekueri sebenarnya membentuk produk silang tiga tabel dan memfilter hasilnya.

Cobalah untuk menggunakan IQueryables sebagai patokan dan buat profil pekerjaan Anda untuk membuat perubahan Anda terukur.

sscheider
sumber
alasannya adalah bahwa ekspresi IQueryable dikonversi ke SQL asli di EF dan dieksekusi tepat di DB sedangkan daftar IEnumerable adalah objek dalam memori. Mereka diambil dari DB di beberapa titik ketika Anda memanggil fungsi agregat seperti Hitung, Jumlah, atau Ke ... dan beroperasi di memori sesudahnya. IQueryables juga terjebak dalam memori setelah Anda memanggil salah satu dari API tersebut tetapi jika tidak, Anda dapat meneruskan ekspresi ke atas tumpukan layer dan bermain-main dengan filter sampai panggilan API. DAL yang dirancang dengan baik sebagai Repositori yang dirancang dengan baik akan menyelesaikan masalah seperti ini;)
Arman McHitarian
10

Saya ingin mengklarifikasi beberapa hal karena tanggapan yang tampaknya saling bertentangan (kebanyakan seputar IEnumerable).

(1) IQueryablememperluas IEnumerableantarmuka. (Anda dapat mengirim IQueryableke sesuatu yang diharapkan IEnumerabletanpa kesalahan.)

(2) Keduanya IQueryabledan IEnumerableLINQ mencoba memuat dengan malas saat iterasi pada set hasil. (Perhatikan bahwa implementasi dapat dilihat dalam metode ekstensi antarmuka untuk setiap jenis.)

Dengan kata lain, IEnumerablestidak secara eksklusif "dalam memori". IQueryablestidak selalu dieksekusi pada database. IEnumerableharus memuat barang ke dalam memori (sekali diambil, mungkin malas) karena tidak memiliki penyedia data abstrak. IQueryablesmengandalkan penyedia abstrak (seperti LINQ-to-SQL), meskipun ini juga bisa menjadi penyedia NET. Memori.

Contoh penggunaan case

(a) Ambil daftar catatan IQueryabledari konteks EF. (Tidak ada catatan di memori.)

(B) Lulus IQueryableke tampilan model siapa IEnumerable. (Valid. IQueryableMeluas IEnumerable.)

(c) Iterate over dan akses catatan set data, entitas anak, dan properti dari tampilan. (Dapat menyebabkan pengecualian!)

Kemungkinan Masalah

(1) IEnumerableUpaya pemuatan malas dan konteks data Anda kedaluwarsa. Pengecualian dilemparkan karena penyedia tidak lagi tersedia.

(2) Proksi entitas Kerangka Entitas diaktifkan (default), dan Anda berupaya mengakses objek terkait (virtual) dengan konteks data yang kedaluwarsa. Sama seperti (1).

(3) Beberapa Set Hasil Aktif (MARS). Jika Anda iterasi atas IEnumerabledalam foreach( var record in resultSet )blok dan secara bersamaan mencoba untuk akses record.childEntity.childProperty, Anda mungkin berakhir dengan MARS karena malas loading dari kedua set data dan entitas relasional. Ini akan menyebabkan pengecualian jika tidak diaktifkan di string koneksi Anda.

Larutan

  • Saya telah menemukan bahwa mengaktifkan MARS di string koneksi berfungsi tidak dapat diandalkan. Saya sarankan Anda menghindari MARS kecuali itu dipahami dengan baik dan secara eksplisit diinginkan.

Jalankan permintaan dan simpan hasil dengan memohon resultList = resultSet.ToList() Ini tampaknya menjadi cara paling mudah untuk memastikan entitas Anda berada di memori.

Dalam kasus di mana Anda mengakses entitas terkait, Anda mungkin masih memerlukan konteks data. Entah itu, atau Anda dapat menonaktifkan proxy entitas dan Includeentitas terkait yang eksplisit dari Anda DbSet.


sumber
9

Perbedaan utama antara "IEnumerable" dan "IQueryable" adalah tentang di mana logika filter dijalankan. Satu dijalankan di sisi klien (dalam memori) dan yang lainnya dijalankan pada database.

Sebagai contoh, kita dapat mempertimbangkan contoh di mana kita memiliki 10.000 catatan untuk pengguna dalam basis data kita dan katakan saja hanya 900 yang merupakan pengguna aktif, jadi dalam kasus ini jika kita menggunakan "IEnumerable" maka pertama-tama memuat semua 10.000 catatan dalam memori dan kemudian menerapkan filter IsActive di atasnya yang akhirnya mengembalikan 900 pengguna aktif.

Sementara di sisi lain pada kasus yang sama jika kita menggunakan "IQueryable" itu akan langsung menerapkan filter IsActive pada database yang langsung dari sana akan mengembalikan 900 pengguna aktif.

Tautan Referensi

Tabish Usman
sumber
mana yang dioptimalkan dan ringan dalam hal kinerja?
Sitecore Sam
@ Sam "IQueryable" lebih disukai dalam hal dioptimalkan dan ringan.
Tabish Usman
6

Kita dapat menggunakan keduanya dengan cara yang sama, dan mereka hanya berbeda dalam kinerjanya.

IQueryable hanya mengeksekusi terhadap database secara efisien. Ini berarti bahwa ia menciptakan seluruh kueri pemilihan dan hanya mendapatkan catatan terkait.

Misalnya, kami ingin mengambil 10 pelanggan teratas yang namanya dimulai dengan 'Nimal'. Dalam hal ini kueri pemilihan akan dihasilkan sebagai select top 10 * from Customer where name like ‘Nimal%’.

Tetapi jika kita menggunakan IEnumerable, kueri akan seperti select * from Customer where name like ‘Nimal%’dan sepuluh teratas akan difilter pada level pengkodean C # (ia mendapatkan semua catatan pelanggan dari basis data dan meneruskannya ke dalam C #).

pengguna3710357
sumber
5

Selain 2 jawaban pertama yang sangat baik (oleh driis & oleh Jacob):

Antarmuka IEnumerable ada di System.Collections namespace.

Objek IEnumerable mewakili satu set data dalam memori dan dapat bergerak maju hanya pada data ini. Kueri yang diwakili oleh objek IEnumerable dijalankan segera dan sepenuhnya, sehingga aplikasi menerima data dengan cepat.

Ketika kueri dieksekusi, IEnumerable memuat semua data, dan jika kita perlu menyaringnya, penyaringan itu sendiri dilakukan di sisi klien.

Antarmuka IQueryable terletak di namespace System.Linq.

Objek IQueryable menyediakan akses jarak jauh ke database dan memungkinkan Anda menavigasi data baik dalam urutan langsung dari awal hingga akhir, atau dalam urutan terbalik. Dalam proses membuat kueri, objek yang dikembalikan adalah IQueryable, kueri dioptimalkan. Akibatnya, lebih sedikit memori yang dikonsumsi selama eksekusi, lebih sedikit bandwidth jaringan, tetapi pada saat yang sama dapat diproses sedikit lebih lambat dari permintaan yang mengembalikan objek IEnumerable.

Apa yang harus dipilih?

Jika Anda memerlukan seluruh rangkaian data yang dikembalikan, maka lebih baik menggunakan IEnumerable, yang menyediakan kecepatan maksimum.

Jika Anda TIDAK membutuhkan seluruh rangkaian data yang dikembalikan, tetapi hanya beberapa data yang difilter, maka lebih baik menggunakan IQueryable.

Gleb B
sumber
0

Selain hal di atas, menarik untuk dicatat bahwa Anda bisa mendapatkan pengecualian jika Anda menggunakan IQueryablealih-alih IEnumerable:

Berikut ini bekerja dengan baik jika productsmerupakan IEnumerable:

products.Skip(-4);

Namun jika itu productsadalah IQueryabledan sedang mencoba mengakses catatan dari tabel DB, maka Anda akan mendapatkan kesalahan ini:

Offset yang ditentukan dalam klausa OFFSET mungkin tidak negatif.

Ini karena kueri berikut dibuat:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

dan OFFSET tidak dapat memiliki nilai negatif.

David Klempfner
sumber