Sintaks Query LINQ

Query LINQ bergaya VB dengan From, Where, dan Select.

Daftar isi

LINQ (Language Integrated Query) menanamkan kemampuan query ke dalam bahasa Visual Basic, sehingga penyaringan dan pemilihan data ditulis sebagai bagian dari bahasanya sendiri. Topik ini membahas bentuk query yang paling mendasar, yaitu From, Where, dan Select, atas array maupun List(Of T) yang berisi kelas sederhana. Pembahasan juga mencakup deferensi eksekusi (deferred execution) dan perbandingannya dengan perulangan manual, sehingga keputusan kapan query lebih tepat digunakan dapat diambil dengan dasar yang jelas.

Penyaringan dengan perulangan manual#

Cara konvensional menyaring data adalah menelusuri koleksi dengan For Each, lalu mencetak atau menampung elemen yang lolos percabangan If. Pola ini bekerja dengan baik, tetapi logika penyaringan menyatu di dalam tubuh perulangan dan hasilnya perlu ditampung pada variabel tambahan. Program berikut menampilkan skor ujian yang mencapai 80 ke atas.

vb Contoh: penyaringan dengan perulangan manual
Module DemoLoop

    Sub Main()
        Dim skor() As Integer = {88, 72, 95, 64, 80}

        For Each nilai As Integer In skor
            If nilai >= 80 Then
                Console.WriteLine(nilai)
            End If
        Next
    End Sub

End Module
text Keluaran
88
95
80

Perulangan tersebut membaca kelima elemen dan bercabang di dalam tubuhnya, sehingga maksud program, yaitu mengambil skor tinggi, baru terbaca setelah kedua struktur itu diikuti. Query LINQ menyatakan maksud yang sama dalam satu bentuk yang terpisah dari mekanisme penelusurannya.

Bentuk query From, Where, dan Select#

Query LINQ dalam VB tersusun atas klausa yang dibaca dari kiri ke kanan. From mendeklarasikan variabel rentang (range variable) yang mewakili satu elemen sumber pada setiap langkah, Where menyaring elemen dengan kondisi Boolean, dan Select menentukan bentuk setiap baris hasil. Bentuknya sebagai berikut.

vb
' tidak-dijalankan
Dim tinggi = From n In nilai
             Where n.Skor >= 80
             Select n.Nama

Variabel n hanya dikenali di dalam query, sedangkan tinggi menampung query yang sudah terbentuk. Baris hasilnya berupa deretan String karena Select n.Nama memproyeksikan setiap elemen ke properti Nama. Diagram berikut menggambarkan alurnya.

nilaiAli 88Budi 72Citra 95Dewi 64Eka 80Where n.Skor >= 80Ali 88Budi 72Citra 95Dewi 64Eka 80abu-abu berarti tidak lolosSelect n.NamaAliCitraEkabaris hasil bertipe String
Alur From, Where, dan Select atas lima baris data

Klausa Where menerima kondisi yang hasilnya True atau False, sehingga elemen Budi dengan skor 72 dan Dewi dengan skor 64 tidak lolos. Klausa Select kemudian membentuk baris hasil tanpa mengubah sumbernya, dan daftar asal tetap berisi lima elemen setelah query selesai.

Query atas kelas sederhana#

Sumber query tidak terbatas pada bilangan dan teks. Kelas sederhana berikut digunakan di sepanjang modul ini sebagai data contoh, yaitu kelas NilaiUjian dengan dua properti otomatis, Nama bertipe String dan Skor bertipe Integer. Koleksinya berupa List(Of NilaiUjian) yang diisi sembilan siswa.

vb Program.vb
Module Program

    Class NilaiUjian
        Public Property Nama As String
        Public Property Skor As Integer
    End Class

    Sub Main()
        Dim nilai As New List(Of NilaiUjian) From {
            New NilaiUjian With {.Nama = "Ali", .Skor = 88},
            New NilaiUjian With {.Nama = "Budi", .Skor = 72},
            New NilaiUjian With {.Nama = "Citra", .Skor = 95},
            New NilaiUjian With {.Nama = "Dewi", .Skor = 64},
            New NilaiUjian With {.Nama = "Eka", .Skor = 80},
            New NilaiUjian With {.Nama = "Fajar", .Skor = 58},
            New NilaiUjian With {.Nama = "Gita", .Skor = 90},
            New NilaiUjian With {.Nama = "Hana", .Skor = 76},
            New NilaiUjian With {.Nama = "Indra", .Skor = 88}
        }

        Dim tinggi = From n In nilai
                     Where n.Skor >= 80
                     Select n.Nama

        For Each nama As String In tinggi
            Console.WriteLine(nama)
        Next
    End Sub

End Module
text Keluaran
Ali
Citra
Eka
Gita
Indra

Query tinggi menghasilkan lima nama, yaitu Ali, Citra, Eka, Gita, dan Indra, karena kelimanya memperoleh skor 80 ke atas. Proyeksi Select n.Nama membuat baris hasil bertipe String sehingga For Each langsung mencetak namanya. Apabila proyeksinya diganti menjadi Select n, baris hasilnya berupa objek NilaiUjian utuh dan properti lainnya tetap dapat dibaca.

Demo berikut menyimulasikan alur From, Where, dan Select atas daftar yang sama. Centang pilihan penyaringan dan proyeksinya, kemudian amati baris hasil beserta bentuk query-nya.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Demo From, Where, dan Select</title>
<style>
  body { font-family: sans-serif; font-size: 14px; margin: 0; padding: 12px; color: #1f3554; }
  .baksan { display: flex; gap: 16px; flex-wrap: wrap; }
  table { border-collapse: collapse; }
  td, th { border: 1px solid #9db4d6; padding: 3px 10px; text-align: left; }
  th { background: #f6f8fc; }
  .panel { flex: 1 1 240px; }
  label { display: block; margin: 6px 0; }
  code.query { display: block; background: #f6f8fc; border: 1px solid #9db4d6; padding: 8px; white-space: pre; font-size: 12.5px; }
  .keluaran { background: #10141b; color: #d7e3f4; padding: 8px; min-height: 96px; white-space: pre; font-family: monospace; font-size: 12.5px; }
  .judul { font-weight: bold; margin: 10px 0 4px; }
</style>
</head>
<body>
<div class="baksan">
  <div>
    <div class="judul">Data: nilai (List(Of NilaiUjian))</div>
    <table id="tabel"></table>
  </div>
  <div class="panel">
    <div class="judul">Pilihan query</div>
    <label><input type="checkbox" id="saring" checked> Where n.Skor &gt;= 80</label>
    <label><input type="checkbox" id="proyeksi"> Select n.Nama</label>
    <div class="judul">Query yang terbentuk</div>
    <code class="query" id="query"></code>
    <div class="judul">Keluaran</div>
    <div class="keluaran" id="keluaran"></div>
  </div>
</div>
<script>
  var nilai = [
    { nama: "Ali", skor: 88 },
    { nama: "Budi", skor: 72 },
    { nama: "Citra", skor: 95 },
    { nama: "Dewi", skor: 64 },
    { nama: "Eka", skor: 80 },
    { nama: "Fajar", skor: 58 },
    { nama: "Gita", skor: 90 },
    { nama: "Hana", skor: 76 },
    { nama: "Indra", skor: 88 }
  ];

  var tabel = document.getElementById("tabel");
  var kepala = tabel.insertRow();
  ["Nama", "Skor"].forEach(function (t) {
    var th = document.createElement("th");
    th.textContent = t;
    kepala.appendChild(th);
  });
  nilai.forEach(function (n) {
    var baris = tabel.insertRow();
    baris.insertCell().textContent = n.nama;
    baris.insertCell().textContent = n.skor;
  });

  function jalankan() {
    var saring = document.getElementById("saring").checked;
    var proyeksi = document.getElementById("proyeksi").checked;

    var kode = "Dim hasil = From n In nilai\n";
    if (saring) { kode += "            Where n.Skor >= 80\n"; }
    kode += proyeksi ? "            Select n.Nama" : "            Select n";
    document.getElementById("query").textContent = kode;

    var hasil = nilai.slice();
    if (saring) { hasil = hasil.filter(function (n) { return n.skor >= 80; }); }
    if (proyeksi) {
      hasil = hasil.map(function (n) { return n.nama; });
      document.getElementById("keluaran").textContent = hasil.join("\n");
    } else {
      document.getElementById("keluaran").textContent =
        hasil.map(function (n) { return n.nama + " " + n.skor; }).join("\n");
    }
  }

  document.getElementById("saring").addEventListener("change", jalankan);
  document.getElementById("proyeksi").addEventListener("change", jalankan);
  jalankan();
</script>
</body>
</html>

Deferensi eksekusi#

Variabel query menyimpan definisi, bukan hasil. Eksekusinya menunggu penelusuran, perilaku yang disebut deferensi eksekusi (deferred execution), dan setiap penelusuran membaca kondisi koleksi terkini. Program berikut menambahkan satu siswa setelah query didefinisikan.

vb Contoh: query bereaksi terhadap perubahan data
Module DemoDeferensi

    Class NilaiUjian
        Public Property Nama As String
        Public Property Skor As Integer
    End Class

    Sub Main()
        Dim nilai As New List(Of NilaiUjian) From {
            New NilaiUjian With {.Nama = "Ali", .Skor = 88},
            New NilaiUjian With {.Nama = "Budi", .Skor = 72},
            New NilaiUjian With {.Nama = "Citra", .Skor = 95},
            New NilaiUjian With {.Nama = "Eka", .Skor = 80}
        }

        Dim tinggi = From n In nilai
                     Where n.Skor >= 80
                     Select n

        Console.WriteLine($"Saat query didefinisikan: {tinggi.Count()}")
        nilai.Add(New NilaiUjian With {.Nama = "Indra", .Skor = 88})
        Console.WriteLine($"Setelah Indra ditambahkan: {tinggi.Count()}")
    End Sub

End Module
text Keluaran
Saat query didefinisikan: 3
Setelah Indra ditambahkan: 4

Count() mengeksekusi query pada saat dipanggil, sehingga hasil pertamanya adalah 3 karena baru tiga siswa yang skornya mencapai 80. Setelah Indra ditambahkan, penelusuran yang sama menghasilkan 4, artinya query menggunakan isi daftar yang terbaru dan bukan potret saat definisinya ditulis. Perilaku ini menguntungkan karena query tidak perlu didefinisikan ulang ketika datanya berubah.

Perbandingan dengan perulangan manual#

Kedua pendekatan menghasilkan program yang benar, dan perbedaannya terletak pada bentuk penulisannya. Tabel berikut merangkum pembeda utamanya.

AspekPerulangan manualQuery LINQ
Tempat logika penyaringanPercabangan If di dalam tubuh perulanganKlausa Where yang berdiri sendiri
Pembentukan hasilVariabel penampung yang dikelola sendiriSelect langsung menyatakan baris hasil
Alur bertahapBertingkat dan menyatu dengan mekanisme penelusuranSetiap tahap menjadi klausa tersendiri
Tipe variabel rentangDitulis eksplisit pada For EachDisimpulkan compiler dari tipe sumbernya

Perulangan manual tetap tepat ketika setiap elemen membutuhkan perlakuan yang berbeda-beda, misalnya pencabangan bertingkat per elemen. Query lebih padat ketika alurnya berupa penyaringan dan proyeksi yang berurutan, karena tiap tahap terbaca sebagai satu klausa. Keduanya dapat dipadukan dalam satu program tanpa masalah.

Kesalahan umum#

Tiga kesalahan berikut berkait erat dengan perilaku query yang baru diperkenalkan. Pemahaman deferensi eksekusi menutup sebagian besar di antaranya.

  1. Mengira variabel query menyimpan salinan data. Data yang ditambahkan setelah query didefinisikan ikut terbaca pada penelusuran berikutnya, sebagaimana ditunjukkan pada bagian Deferensi eksekusi. Bila potret tetap diinginkan, panggil ToList atau ToArray.
  2. Membandingkan tipe yang tidak cocok pada Where. Kondisi n.Nama >= 80 membandingkan String dengan Integer dan ditolak compiler ketika Option Strict On aktif. Pastikan properti yang dibandingkan bertipe angka, misalnya n.Skor.
  3. Menggunakan variabel rentang di luar query. n hanya dikenali di dalam klausa yang mendeklarasikannya, sehingga penulisan n.Skor setelah query selesai ditolak compiler sebagai variabel yang tidak dideklarasikan.

Ringkasan#

  • Query LINQ menyusun penyaringan dan proyeksi sebagai klausa, yaitu From untuk variabel rentang, Where untuk kondisi Boolean, dan Select untuk bentuk baris hasil.
  • Sumber query dapat berupa array maupun List(Of T) yang berisi kelas sederhana, dan tipe baris hasil mengikuti bentuk Select.
  • Eksekusi query menunggu penelusuran, sehingga hasilnya selalu menggunakan kondisi data terkini.
  • Perulangan manual tetap digunakan untuk perlakuan per elemen yang bercabang, sedangkan query lebih padat untuk alur penyaringan berurutan.

Referensi#