Entity Framework Core (EF Core) adalah Object-Relational Mapper (ORM) resmi dari Microsoft yang memungkinkan developer berinteraksi dengan database relasional menggunakan object dan method C#, tanpa perlu menulis query SQL mentah untuk sebagian besar operasi CRUD sehari-hari.
EF Core bekerja dengan konsep DbContext sebagai representasi sesi koneksi ke database, dan DbSet
Artikel ini membahas cara mengatur DbContext dan model dasar, melakukan operasi CRUD sederhana, serta memahami bagaimana EF Core menerjemahkan kode LINQ menjadi query SQL yang dijalankan terhadap database.
Mendefinisikan DbContext dan Model
DbContext merepresentasikan sesi koneksi dan DbSet mewakili tabel
Operasi Create dan Read
Add dan SaveChanges untuk create, LINQ untuk read
Operasi Update dan Delete
EF Core melacak perubahan object secara otomatis
Melihat Query SQL yang Dihasilkan EF Core
LogTo menampilkan SQL asli yang dihasilkan EF Core
Tracking vs No-Tracking Query pada EF Core
AsNoTracking() menghilangkan overhead change tracking untuk query read-only
Secara default, setiap entity yang diambil lewat DbContext akan dipantau oleh change tracker EF Core agar perubahan pada propertinya bisa terdeteksi otomatis ketika SaveChangesAsync dipanggil. Mekanisme ini sangat berguna untuk operasi update, tetapi menimbulkan overhead memori dan CPU yang tidak perlu ketika data hanya akan ditampilkan ke user tanpa pernah diubah, seperti pada endpoint listing atau laporan.
Memanggil AsNoTracking() sebelum eksekusi query memberi tahu EF Core untuk tidak menyertakan entity hasil query tersebut ke dalam change tracker, sehingga proses query menjadi lebih cepat dan konsumsi memori lebih rendah, terutama saat mengambil ratusan atau ribuan baris sekaligus. Sebagai patokan praktis, setiap endpoint GET yang murni menampilkan data sebaiknya selalu menggunakan AsNoTracking() secara konsisten.
Untuk aplikasi yang sebagian besar operasinya bersifat baca, EF Core juga menyediakan opsi mengubah perilaku default tracking di level DbContext lewat ChangeTracker.QueryTrackingBehavior, sehingga tidak perlu menambahkan AsNoTracking() di setiap query satu per satu, dan hanya query yang benar-benar butuh update yang secara eksplisit memanggil AsTracking().
Raw SQL Query dengan FromSqlRaw dan ExecuteSqlRaw
Placeholder {0} dan {1} diterjemahkan menjadi parameterized query, bukan string concat
LINQ to Entities pada EF Core mampu menangani sebagian besar kebutuhan query, tetapi ada kasus tertentu seperti laporan dengan agregasi kompleks, penggunaan hint index, atau pemanggilan stored procedure lama yang lebih efisien ditulis langsung sebagai SQL mentah. FromSqlRaw memungkinkan eksekusi SQL mentah yang hasilnya tetap dipetakan ke entity class EF Core, sehingga hasil query tetap bisa diproses lebih lanjut sebagai objek C# seperti hasil LINQ biasa. Kolom hasil query SQL juga harus mencakup semua properti yang dipetakan pada entity tersebut, atau EF Core akan melempar error saat mencoba membentuk object dari hasil baris yang dikembalikan.
Penulisan parameter wajib memakai placeholder seperti {0} dan {1} yang diisi lewat argumen method, bukan string interpolation langsung seperti $"WHERE Status = '{status}'", karena pola placeholder ini diterjemahkan EF Core menjadi parameterized query yang aman dari serangan SQL Injection. Menggabungkan nilai user secara manual ke dalam string SQL tetap berisiko tinggi walau dibungkus FromSqlRaw sekalipun. Hasil dari FromSqlRaw juga masih bisa dirangkai lebih lanjut dengan operator LINQ seperti Where atau OrderBy selama tidak memakai Select yang mengubah bentuk entity hasilnya.
ExecuteSqlRawAsync dipakai untuk perintah yang tidak mengembalikan baris data seperti UPDATE atau DELETE massal yang sulit diekspresikan secara efisien lewat LINQ biasa, dan hasilnya berupa jumlah baris yang terpengaruh, bukan koleksi entity. Kedua method ini sebaiknya dipakai secara selektif untuk kasus yang benar-benar membutuhkan kontrol SQL eksplisit, karena query raw kehilangan sebagian keuntungan EF Core seperti type safety penuh dan kemudahan refactoring otomatis saat nama kolom berubah. Perubahan lewat ExecuteSqlRawAsync juga langsung dieksekusi ke database tanpa melewati change tracker, sehingga entity yang sudah dimuat sebelumnya di memori tidak otomatis ikut diperbarui.
Global Query Filter untuk Soft Delete yang Konsisten di Semua Query
Soft delete sering diimplementasikan dengan menambah kolom IsDeleted dan mengandalkan setiap developer menambahkan filter Where secara manual pada setiap query yang mengambil data entity tersebut. Pendekatan ini cukup rawan terlewat, terutama saat ada query baru yang ditulis oleh anggota tim yang belum tahu konvensi tersebut, sehingga data yang seharusnya tersembunyi dari pengguna tetap saja ikut muncul di hasil query.
HasQueryFilter mendaftarkan aturan penyaringan itu sekali saja di level model sehingga berlaku otomatis untuk semua query LINQ terhadap entity tersebut, termasuk query baru yang ditulis belakangan di masa depan. Untuk kasus tertentu yang memang membutuhkan akses ke data yang sudah terhapus, misalnya fitur audit atau pemulihan data, filter ini bisa dilewati sementara dengan memanggil IgnoreQueryFilters pada query spesifik yang membutuhkannya.
Kesalahan Umum yang Sering Terjadi
Lupa Memanggil SaveChanges Setelah Add atau Remove
Perubahan yang dilakukan dengan Add, Remove, atau mengubah property object hanya tersimpan di memori sampai SaveChanges benar-benar dipanggil; lupa memanggilnya akan membuat perubahan tersebut tidak pernah tersimpan ke database.
Membuat DbContext Baru untuk Setiap Operasi Kecil
Membuat instance DbContext baru secara manual berulang kali alih-alih memanfaatkan dependency injection dengan lifetime Scoped bisa menyebabkan masalah koneksi database yang tidak dikelola dengan efisien.
Mengambil Seluruh Data ke Memori Sebelum Memfilter
Memanggil ToList() terlalu dini sebelum menerapkan Where akan membuat EF Core mengambil seluruh baris tabel ke memori terlebih dahulu baru memfilternya di sisi aplikasi, alih-alih menerjemahkan filter tersebut menjadi klausa WHERE pada query SQL yang jauh lebih efisien.
Kapan Pendekatan Ini Tidak Cocok Dipakai
EF Core memberikan produktivitas tinggi karena developer bisa bekerja dengan object C# dan LINQ tanpa menulis SQL manual, sangat mempercepat development untuk operasi CRUD standar.
Namun untuk query yang sangat kompleks atau membutuhkan optimasi ekstrem, SQL mentah atau stored procedure terkadang masih memberikan kontrol dan performa yang lebih baik dibanding query LINQ yang diterjemahkan otomatis, sehingga EF Core tetap menyediakan opsi raw SQL untuk kasus-kasus khusus seperti ini.
Ringkasan
Entity Framework Core menyederhanakan interaksi dengan database relasional lewat DbContext dan DbSet yang bisa di-query dengan LINQ, menghilangkan kebutuhan menulis SQL manual untuk operasi CRUD sehari-hari. Pahami change tracking dan kapan memanggil SaveChanges, serta manfaatkan logging untuk memahami SQL yang sebenarnya dihasilkan di balik layar.
Seri Belajar .NET Core:
← Sebelumnya: Konfigurasi appsettings.json dan Environment
→ Selanjutnya: Migration dan Seeding Data dengan EF Core