Pengantar Kueri LINQ di C#

Kueri adalah ekspresi yang mengambil data dari sumber data. Sumber data yang berbeda memiliki bahasa kueri asli yang berbeda, misalnya SQL untuk database relasional dan XQuery untuk XML. Pengembang harus mempelajari bahasa kueri baru untuk setiap jenis sumber data atau format data yang harus mereka dukung. LINQ menyederhanakan situasi ini dengan menawarkan model bahasa C# yang konsisten untuk jenis sumber dan format data. Dalam kueri LINQ, Anda selalu bekerja dengan objek C#. Anda menggunakan pola pengkodian dasar yang sama untuk mengkueri dan mengubah data dalam dokumen XML, database SQL, koleksi .NET, dan format lainnya saat penyedia LINQ tersedia.

Tiga Bagian Dari Operasi Kueri

Semua operasi kueri LINQ terdiri dari tiga tindakan berbeda:

  1. Dapatkan sumber data.
  2. Buat kueri.
  3. Mengeksekusi kueri.

Contoh berikut menunjukkan bagaimana tiga bagian operasi kueri dinyatakan dalam kode sumber. Contoh ini menggunakan array bilangan bulat sebagai sumber data untuk kenyamanan; namun, konsep yang sama juga berlaku untuk sumber data lain. Contoh ini disebut di seluruh sisa artikel ini.

// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];

// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

// 3. Query execution.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

Ilustrasi berikut menunjukkan operasi kueri lengkap. Di LINQ, eksekusi kueri berbeda dari kueri itu sendiri. Dengan kata lain, Anda tidak mengambil data apa pun dengan membuat variabel kueri.

Diagram operasi kueri LINQ lengkap.

Sumber Data

Sumber data dalam contoh sebelumnya adalah array, yang mendukung antarmuka generik IEnumerable<T> . Fakta ini berarti dapat dikueri dengan LINQ. Kueri dijalankan dalam pernyataan foreach, dan foreach memerlukan IEnumerable atau IEnumerable<T>. Jenis yang mendukung IEnumerable<T> atau antarmuka turunan seperti IQueryable<T> generik disebut jenis yang dapat dikueri.

Jenis yang dapat dikueri tidak memerlukan modifikasi atau perlakuan khusus untuk berfungsi sebagai sumber data LINQ. Jika data sumber belum dalam memori sebagai jenis yang dapat dikueri, penyedia LINQ harus mewakilinya. Misalnya, LINQ ke XML memuat dokumen XML ke dalam jenis XElement yang dapat dikueri:

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Dengan EntityFramework, Anda membuat pemetaan hubungan objek antara kelas C# dan skema database Anda. Anda menulis kueri anda terhadap objek, dan pada run-time EntityFramework menangani komunikasi dengan database. Dalam contoh berikut, Customers mewakili tabel tertentu dalam database, dan jenis hasil kueri, IQueryable<T>, berasal dari IEnumerable<T>.

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

Untuk informasi selengkapnya tentang cara membuat jenis sumber data tertentu, lihat dokumentasi untuk berbagai penyedia LINQ. Namun, aturan dasarnya sederhana: sumber data LINQ adalah objek apa pun yang mendukung antarmuka generik IEnumerable<T> , atau antarmuka yang mewarisinya, biasanya IQueryable<T>.

Catatan

Jenis seperti ArrayList yang mendukung antarmuka non-generik IEnumerable juga dapat digunakan sebagai sumber data LINQ. Untuk informasi selengkapnya, lihat Cara kueri ArrayList dengan LINQ.

Kueri

Kueri menentukan informasi apa yang akan diambil dari sumber data atau sumber. Secara opsional, kueri juga menentukan bagaimana informasi tersebut harus diurutkan, dikelompokkan, dan dibentuk sebelum dikembalikan. Kueri disimpan dalam variabel kueri dan diinisialisasi dengan ekspresi kueri. Anda menggunakan sintaks kueri C# untuk menulis kueri.

Kueri dalam contoh sebelumnya mengembalikan semua bilangan genap dari array bilangan bulat. Ekspresi kueri berisi tiga klausa: from, where dan select. (Jika Anda terbiasa dengan SQL, Anda melihat bahwa urutan klausul dibalik dari urutan dalam SQL.) Klausa from menentukan sumber data, where klausul menerapkan filter, dan select klausa menentukan jenis elemen yang dikembalikan. Semua klausa kueri dibahas secara rinci di bagian ini. Untuk saat ini, poin pentingnya adalah bahwa dalam LINQ, variabel kueri itu sendiri tidak mengambil tindakan dan tidak mengembalikan data. Ini hanya menyimpan informasi yang diperlukan untuk menghasilkan hasil ketika kueri dijalankan di beberapa titik kemudian. Untuk informasi selengkapnya tentang bagaimana kueri dibuat, lihat Gambaran Umum Operator Kueri Standar (C#).

Catatan

Kueri juga dapat diekspresikan dengan menggunakan sintaks metode. Untuk informasi selengkapnya, lihat Sintaksis kueri dan sintaks metode di LINQ.

Klasifikasi operator kueri standar dengan cara eksekusi

Implementasi LINQ ke Objek dari metode operator kueri standar dijalankan dengan salah satu dari dua cara utama: segera atau ditangguhkan. Operator kueri yang menggunakan eksekusi yang ditangguhkan juga dapat dibagi menjadi dua kategori: streaming dan nonstreaming.

Segera

Eksekusi langsung berarti bahwa sumber data dibaca dan operasi dilakukan sekali. Semua operator kueri standar yang segera mengembalikan hasil skalar. Contoh kueri tersebut adalah Count, Max, Average, dan First. Metode ini dijalankan tanpa pernyataan eksplisit foreach karena kueri itu sendiri harus digunakan foreach untuk mengembalikan hasil. Kueri ini mengembalikan satu nilai, bukan IEnumerable koleksi. Anda dapat memaksa kueri apa pun untuk segera dijalankan menggunakan Enumerable.ToList metode atau Enumerable.ToArray . Eksekusi langsung menyediakan penggunaan kembali hasil kueri, bukan deklarasi kueri. Hasilnya diambil sekali, lalu disimpan untuk digunakan di masa mendatang. Kueri berikut mengembalikan hitungan angka genap dalam array sumber:

var evenNumQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

Untuk memaksa eksekusi langsung kueri apa pun dan menyimpan hasilnya, Anda dapat memanggil metode ToList atau ToArray.

List<int> numQuery2 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
    (from num in numbers
        where (num % 2) == 0
        select num).ToArray();

Anda juga dapat memaksa eksekusi dengan menempatkan perulangan foreach segera setelah ekspresi kueri. Namun, dengan memanggil ToList atau ToArray Anda juga menyimpan semua dalam satu objek koleksi.

Ditangguhkan

Eksekusi yang ditangguhkan berarti bahwa operasi tidak dilakukan pada titik dalam kode tempat kueri dideklarasikan. Operasi hanya dilakukan saat variabel kueri dijumlahkan, misalnya dengan menggunakan pernyataan foreach. Hasil menjalankan kueri bergantung pada konten sumber data saat kueri dijalankan daripada saat kueri ditentukan. Jika variabel kueri dijumlahkan beberapa kali, hasilnya mungkin berbeda setiap saat. Hampir semua operator kueri standar yang jenis pengembaliannya adalah IEnumerable<T> atau IOrderedEnumerable<TElement> dijalankan dengan cara yang ditangguhkan. Eksekusi yang ditangguhkan menyediakan fasilitas penggunaan kembali kueri karena kueri mengambil data yang diperbarui dari sumber data setiap kali hasil kueri diulang. Kode berikut menunjukkan contoh eksekusi yang ditangguhkan:

foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

Pernyataan foreach ini juga di mana hasil kueri diambil. Misalnya, dalam kueri sebelumnya, variabel iterasi num menyimpan setiap nilai (satu per satu) dalam urutan yang dikembalikan.

Karena variabel kueri itu sendiri tidak pernah menyimpan hasil kueri, Anda dapat menjalankannya berulang kali untuk mengambil data yang diperbarui. Misalnya, aplikasi terpisah mungkin terus memperbarui database. Dalam aplikasi, Anda dapat membuat satu kueri yang mengambil data terbaru, dan Anda dapat menjalankannya pada interval untuk mengambil hasil yang diperbarui.

Operator kueri yang menggunakan eksekusi yang ditangguhkan juga dapat diklasifikasikan sebagai streaming atau nonstreaming.

Streaming

Operator streaming tidak perlu membaca semua data sumber sebelum menghasilkan elemen. Pada saat eksekusi, operator streaming melakukan operasinya pada setiap elemen sumber saat dibaca dan menangguhkan elemen jika sesuai. Operator streaming terus membaca elemen sumber hingga elemen hasil dapat diproduksi. Ini berarti bahwa lebih dari satu elemen sumber mungkin dibaca untuk menghasilkan satu elemen hasil.

Non-aliran

Operator nonstreaming harus membaca semua data sumber sebelum dapat menghasilkan elemen hasil. Operasi seperti pengurutan atau pengelompokan termasuk dalam kategori ini. Pada saat eksekusi, operator kueri non-aliran membaca semua data sumber, memasukkannya ke dalam struktur data, melakukan operasi, dan menghasilkan elemen yang dihasilkan.

Tabel klasifikasi

Tabel berikut mengklasifikasikan setiap metode operator kueri standar sesuai dengan metode eksekusinya.

Catatan

Jika operator ditandai dalam dua kolom, dua urutan input terlibat dalam operasi, dan setiap urutan dievaluasi secara berbeda. Dalam kasus ini, selalu merupakan urutan pertama dalam daftar parameter yang dievaluasi dengan cara streaming yang ditangguhkan.

Operator kueri standar Tipe hasil Eksekusi segera Eksekusi streaming yang ditangguhkan Eksekusi non-streaming yang ditangguhkan
Aggregate TSource X
All Boolean X
Any Boolean X
AsEnumerable IEnumerable<T> X
Average Nilai numerik tunggal X
Cast IEnumerable<T> X
Concat IEnumerable<T> X
Contains Boolean X
Count Int32 X
DefaultIfEmpty IEnumerable<T> X
Distinct IEnumerable<T> X
ElementAt TSource X
ElementAtOrDefault TSource? X
Empty IEnumerable<T> X
Except IEnumerable<T> X X
First TSource X
FirstOrDefault TSource? X
GroupBy IEnumerable<T> X
GroupJoin IEnumerable<T> X X
Intersect IEnumerable<T> X X
Join IEnumerable<T> X X
Last TSource X
LastOrDefault TSource? X
LongCount Int64 X
Max Nilai numerik tunggal, TSource, atau TResult? X
Min Nilai numerik tunggal, TSource, atau TResult? X
OfType IEnumerable<T> X
OrderBy IOrderedEnumerable<TElement> X
OrderByDescending IOrderedEnumerable<TElement> X
Range IEnumerable<T> X
Repeat IEnumerable<T> X
Reverse IEnumerable<T> X
Select IEnumerable<T> X
SelectMany IEnumerable<T> X
SequenceEqual Boolean X
Single TSource X
SingleOrDefault TSource? X
Skip IEnumerable<T> X
SkipWhile IEnumerable<T> X
Sum Nilai numerik tunggal X
Take IEnumerable<T> X
TakeWhile IEnumerable<T> X
ThenBy IOrderedEnumerable<TElement> X
ThenByDescending IOrderedEnumerable<TElement> X
ToArray TSource[] array X
ToDictionary Dictionary<TKey,TValue> X
ToList IList<T> X
ToLookup ILookup<TKey,TElement> X
Union IEnumerable<T> X
Where IEnumerable<T> X

LINQ ke objek

"LINQ ke Objek" mengacu pada penggunaan kueri LINQ dengan salah satu IEnumerable atau IEnumerable<T> koleksi secara langsung. Anda dapat menggunakan LINQ untuk mengkueri koleksi yang dapat dijumlahkan, seperti List<T>, , Arrayatau Dictionary<TKey,TValue>. Koleksi dapat ditentukan pengguna atau jenis yang dikembalikan oleh .NET API. Dalam pendekatan LINQ, Anda menulis kode deklaratif yang menjelaskan apa yang ingin Anda ambil. LINQ ke Objek memberikan pengantar yang bagus untuk pemrograman dengan LINQ.

Kueri LINQ menawarkan tiga keuntungan utama daripada perulangan tradisional foreach :

  • Mereka lebih ringkas dan mudah dibaca, terutama saat memfilter beberapa kondisi.
  • Mereka menyediakan kemampuan pemfilteran, pengurutan, dan pengelompokan yang kuat dengan minimum kode aplikasi.
  • Mereka dapat diport ke sumber data lain dengan sedikit atau tanpa modifikasi.

Semakin kompleks operasi yang ingin Anda lakukan pada data, semakin banyak manfaat yang Anda wujudkan menggunakan LINQ alih-alih teknik iterasi tradisional.

Menyimpan hasil kueri di dalam memori

Kueri pada dasarnya adalah sekumpulan instruksi tentang cara mengambil dan menata data. Kueri dijalankan sesekali, karena setiap item berikutnya dalam hasil diminta. Saat Anda menggunakan foreach untuk mengiterasi hasilnya, item dikembalikan sebagai diakses. Untuk mengevaluasi kueri dan menyimpan hasilnya tanpa menjalankan perulangan foreach, cukup panggil salah satu metode berikut pada variabel kueri:

Anda harus menetapkan objek koleksi yang dikembalikan ke variabel baru saat menyimpan hasil kueri, seperti yang diperlihatkan dalam contoh berikut:

List<int> numbers = [1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20];

IEnumerable<int> queryFactorsOfFour =
    from num in numbers
    where num % 4 == 0
    select num;

// Store the results in a new variable
// without executing a foreach loop.
var factorsofFourList = queryFactorsOfFour.ToList();

// Read and write from the newly created list to demonstrate that it holds data.
Console.WriteLine(factorsofFourList[2]);
factorsofFourList[2] = 0;
Console.WriteLine(factorsofFourList[2]);

Lihat juga