Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini memperlihatkan pola dasar untuk inisialisasi dan konfigurasi instans DbContext.
Peringatan
Artikel ini menggunakan database lokal yang tidak mengharuskan pengguna untuk diautentikasi. Aplikasi produksi harus menggunakan alur autentikasi paling aman yang tersedia. Untuk informasi selengkapnya tentang autentikasi untuk aplikasi pengujian dan produksi yang disebarkan, lihat Mengamankan alur autentikasi.
Masa pakai DbContext
Masa pakai DbContext dimulai ketika instans dibuat dan berakhir saat instans dibuang.
DbContext Instans dirancang untuk digunakan untuk satuunit kerja. Ini berarti bahwa masa pakai instans DbContext biasanya sangat singkat.
Tip
Untuk mengutip Martin Fowler dari tautan di atas, "Unit Kerja melacak semua yang Anda lakukan selama transaksi bisnis yang dapat memengaruhi database. Setelah selesai, ia mencari tahu semua yang perlu dilakukan untuk mengubah database sebagai hasil dari pekerjaan Anda."
Unit kerja umum saat menggunakan Entity Framework Core (EF Core) melibatkan:
- Pembuatan instans
DbContext - Pelacakan instans entitas berdasarkan konteks. Entitas dilacak oleh
- Menjadi dikembalikan dari kueri
- Menjadi ditambahkan atau dilampirkan ke konteks
- Perubahan dilakukan pada entitas yang dilacak sesuai kebutuhan untuk menerapkan aturan bisnis
- SaveChanges atau SaveChangesAsync dipanggil. EF Core mendeteksi perubahan yang dibuat dan menulisnya ke database.
- Instans
DbContextdibuang
Penting
- Penting untuk membuang DbContext setelah digunakan. Ini memastikan apa pun:
- Sumber daya yang tidak dikelola dibeberkan.
- Peristiwa atau kait lainnya tidak terdaftar. Membatalkan pendaftaran mencegah kebocoran memori ketika instans tetap direferensikan.
- DbContext Tidak aman untuk utas. Jangan bagikan konteks di antara utas. Pastikan untuk menunggu semua panggilan asinkron sebelum terus menggunakan instans konteks.
- Kode InvalidOperationException EF Core yang dilemparkan dapat menempatkan konteks ke dalam status yang tidak dapat dipulihkan. Pengecualian tersebut menunjukkan kesalahan program dan tidak dirancang untuk dipulihkan.
DbContext dalam injeksi dependensi untuk ASP.NET Core
Di banyak aplikasi web, setiap permintaan HTTP sesuai dengan satu unit kerja. Ini membuat mengikat masa pakai konteks dengan permintaan default yang baik untuk aplikasi web.
ASP.NET Core dikonfigurasi menggunakan injeksi dependensi. EF Core dapat ditambahkan ke konfigurasi ini menggunakan AddDbContext di Program.cs. Contohnya:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string"
+ "'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
Kode sebelumnya mendaftar ApplicationDbContext, subkelas dari DbContext, sebagai layanan terlingkup di penyedia layanan aplikasi ASP.NET Core. Penyedia layanan juga dikenal sebagai kontainer injeksi dependensi. Konteks dikonfigurasi untuk menggunakan penyedia database SQL Server dan membaca string koneksi dari konfigurasi ASP.NET Core.
Kelas ApplicationDbContext harus mengekspos konstruktor publik dengan parameter DbContextOptions<ApplicationDbContext>. Beginilah cara konfigurasi konteks dari AddDbContext diteruskan ke DbContext. Contohnya:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
ApplicationDbContext dapat digunakan dalam pengontrol inti ASP.NET atau layanan lain melalui injeksi konstruktor:
public class MyController
{
private readonly ApplicationDbContext _context;
public MyController(ApplicationDbContext context)
{
_context = context;
}
}
Hasil akhir adalah instans ApplicationDbContext yang dibuat untuk setiap permintaan dan diteruskan ke pengontrol untuk melakukan unit kerja sebelum dibuang saat permintaan berakhir.
Baca lebih lanjut di artikel ini untuk mempelajari selengkapnya tentang opsi konfigurasi. Lihat Injeksi dependensi di ASP.NET Core untuk informasi selengkapnya.
Inisialisasi Dasar DbContext dengan 'baru'
DbContext instans dapat dibangun dengan new dalam C#. Konfigurasi dapat dilakukan dengan mengesampingkan metode OnConfiguring, atau dengan meneruskan opsi ke konstruktor. Contohnya:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
Pola ini juga memudahkan untuk meneruskan konfigurasi seperti string koneksi melalui konstruktor DbContext. Contohnya:
public class ApplicationDbContext : DbContext
{
private readonly string _connectionString;
public ApplicationDbContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
}
Secara bergantian, DbContextOptionsBuilder dapat digunakan untuk membuat objek DbContextOptions yang kemudian diteruskan ke konstruktor DbContext. Ini memungkinkan DbContext dikonfigurasi untuk injeksi dependensi juga dibangun secara eksplisit. Misalnya, saat menggunakan ApplicationDbContext yang ditentukan untuk aplikasi web ASP.NET Core di atas:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
DbContextOptions dapat dibuat dan konstruktor dapat dipanggil secara eksplisit:
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0")
.Options;
using var context = new ApplicationDbContext(contextOptions);
Menggunakan pabrik DbContext
Beberapa jenis aplikasi (misalnya ASP.NET Core Blazor) menggunakan injeksi dependensi tetapi tidak membuat cakupan layanan yang selaras dengan masa pakai yang diinginkan DbContext. Bahkan di mana penyelarasan seperti itu memang ada, aplikasi mungkin perlu melakukan beberapa unit kerja dalam cakupan ini. Misalnya, beberapa unit kerja dalam satu permintaan HTTP.
Dalam kasus ini, AddDbContextFactory dapat digunakan untuk mendaftarkan pabrik untuk pembuatan instans DbContext. Contohnya:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"));
}
Kelas ApplicationDbContext harus mengekspos konstruktor publik dengan parameter DbContextOptions<ApplicationDbContext>. Ini adalah pola yang sama seperti yang digunakan di bagian tradisional ASP.NET Core di atas.
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Pabrik DbContextFactory kemudian dapat digunakan di layanan lain melalui injeksi konstruktor. Contohnya:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
Pabrik yang disuntikkan kemudian dapat digunakan untuk membangun instans DbContext dalam kode layanan. Contohnya:
public async Task DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
Perhatikan bahwa DbContext instans yang dibuat dengan cara ini tidak dikelola oleh penyedia layanan aplikasi dan oleh karena itu harus dibuang oleh aplikasi.
Lihat ASP.NET Core Blazor Server dengan Entity Framework Core untuk informasi selengkapnya tentang menggunakan EF Core dengan Blazor.
DbContextOptions
Titik awal untuk semua konfigurasi DbContext adalah DbContextOptionsBuilder. Ada tiga cara untuk mendapatkan penyusun ini:
- Metode
AddDbContextdalam dan terkait - Dalam
OnConfiguring - Dibangun secara eksplisit dengan
new
Contoh setiap ditunjukkan di bagian sebelumnya. Konfigurasi yang sama dapat diterapkan terlepas dari mana pembangun berasal. Selain itu, OnConfiguring selalu dipanggil terlepas dari bagaimana konteks dibangun. Ini berarti OnConfiguring dapat digunakan untuk melakukan konfigurasi tambahan bahkan ketika AddDbContext sedang digunakan.
Mengonfigurasi penyedia database
Setiap instans DbContext harus dikonfigurasi untuk menggunakan satu dan hanya satu penyedia database. (Instans subjenis yang DbContext berbeda dapat digunakan dengan penyedia database yang berbeda, tetapi satu instans hanya boleh menggunakan satu.) Penyedia database dikonfigurasi menggunakan panggilan tertentu Use*. Misalnya, untuk menggunakan penyedia database SQL Server:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
Metode Use* ini adalah metode ekstensi yang diterapkan oleh penyedia database. Ini berarti bahwa paket NuGet penyedia database harus diinstal sebelum metode ekstensi dapat digunakan.
Tip
Penyedia database EF Core memanfaatkan metode ekstensi secara ekstensif. Jika pengkompilasi menunjukkan bahwa metode tidak dapat ditemukan, pastikan bahwa paket NuGet penyedia diinstal dan Anda memiliki using Microsoft.EntityFrameworkCore; dalam kode Anda.
Tabel berikut ini berisi contoh untuk penyedia database umum.
| Sistem database | Konfigurasi contoh | Paket NuGet |
|---|---|---|
| SQL Server atau Azure SQL | .UseSqlServer(connectionString) |
Microsoft.EntityFrameworkCore.SqlServer |
| Azure Cosmos DB | .UseCosmos(connectionString, databaseName) |
Microsoft.EntityFrameworkCore.Cosmos |
| SQLite | .UseSqlite(connectionString) |
Microsoft.EntityFrameworkCore.Sqlite |
| Database dalam memori EF Core | .UseInMemoryDatabase(databaseName) |
Microsoft.EntityFrameworkCore.InMemory |
| PostgreSQL* | .UseNpgsql(connectionString) |
Npgsql.EntityFrameworkCore.PostgreSQL |
| MySQL/MariaDB* | .UseMySql(connectionString) |
Pomelo.EntityFrameworkCore.MySql |
| Oracle* | .UseOracle(connectionString) |
Oracle.EntityFrameworkCore |
*Penyedia database ini tidak dikirim oleh Microsoft. Lihat Penyedia Database untuk informasi selengkapnya tentang penyedia database.
Peringatan
Database dalam memori EF Core tidak dirancang untuk penggunaan produksi. Selain itu, ini mungkin bukan pilihan terbaik bahkan untuk pengujian. Lihat Kode Pengujian yang Menggunakan EF Core untuk informasi selengkapnya.
Lihat String Koneksi untuk informasi selengkapnya tentang menggunakan string koneksi dengan EF Core.
Konfigurasi opsional khusus untuk penyedia database dilakukan di penyusun khusus penyedia tambahan. Misalnya, menggunakan EnableRetryOnFailure untuk mengonfigurasi coba lagi untuk ketahanan koneksi saat menyambungkan ke Azure SQL:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test",
providerOptions => { providerOptions.EnableRetryOnFailure(); });
}
}
Tip
Penyedia database yang sama digunakan untuk SQL Server dan Azure SQL. Namun, disarankan agar ketahanan koneksi digunakan saat menghubungkan ke SQL Azure.
Lihat Penyedia Database untuk informasi selengkapnya tentang konfigurasi khusus penyedia.
Konfigurasi DbContext lainnya
Konfigurasi DbContext lainnya dapat dirantai baik sebelum atau sesudah (tidak ada bedanya yang mana) panggilan Use*. Misalnya, untuk mengaktifkan pengelogan data sensitif:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.EnableSensitiveDataLogging()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
Tabel berikut berisi contoh metode umum yang dipanggil pada DbContextOptionsBuilder.
| Metode DbContextOptionsBuilder | Apa fungsinya | Pelajari lebih lanjut |
|---|---|---|
| UseQueryTrackingBehavior | Mengatur perilaku pelacakan default untuk kueri | Perilaku Pelacakan Kueri |
| LogTo | Cara sederhana untuk mendapatkan log EF Core | Pengelogan, peristiwa, dan diagnostik |
| UseLoggerFactory | Mendaftarkan pabrik Microsoft.Extensions.Logging |
Pengelogan, peristiwa, dan diagnostik |
| EnableSensitiveDataLogging | Menyertakan data aplikasi dalam pengecualian dan pengelogan | Pengelogan, peristiwa, dan diagnostik |
| EnableDetailedErrors | Kesalahan kueri yang lebih rinci (dengan mengorbankan performa) | Pengelogan, peristiwa, dan diagnostik |
| ConfigureWarnings | Abaikan atau lempar untuk peringatan dan peristiwa lainnya | Pengelogan, peristiwa, dan diagnostik |
| AddInterceptors | Mendaftarkan pencegat EF Core | Pengelogan, peristiwa, dan diagnostik |
| EnableServiceProviderCaching | Mengontrol pengelolaan cache penyedia layanan internal | Penyimpanan Cache Penyedia Layanan |
| UseMemoryCache | Mengonfigurasi cache memori yang digunakan oleh EF Core | Integrasi Cache Memori |
| UseLazyLoadingProxies | Gunakan proksi dinamis untuk pemuatan lambat | Pemuatan Lambat |
| UseChangeTrackingProxies | Menggunakan proksi dinamis untuk pelacakan perubahan | Segera hadir... |
Catatan
UseLazyLoadingProxies dan UseChangeTrackingProxies merupakan metode ekstensi dari paket NuGet Microsoft.EntityFrameworkCore.Proxies. Semacam ini ". Panggilan UseSomething()" adalah cara yang disarankan untuk mengonfigurasi dan/atau menggunakan ekstensi EF Core yang terkandung dalam paket lain.
DbContextOptions melawan DbContextOptions<TContext>
Sebagian besar DbContext subkelas yang menerima DbContextOptions harus menggunakan variasi generikDbContextOptions<TContext>. Contohnya:
public sealed class SealedApplicationDbContext : DbContext
{
public SealedApplicationDbContext(DbContextOptions<SealedApplicationDbContext> contextOptions)
: base(contextOptions)
{
}
}
Ini memastikan bahwa opsi yang benar untuk subjenis tertentu DbContext diselesaikan dari injeksi dependensi, bahkan ketika beberapa subjenis DbContext terdaftar.
Tip
DbContext Anda tidak perlu disegel, tetapi penyegelan adalah praktik terbaik untuk melakukannya agar kelas tidak dirancang untuk diwarisi.
Namun, jika subjenis DbContext itu sendiri dimaksudkan untuk diwarisi, maka subjenis tersebut harus mengekspos konstruktor yang dilindungi yang mengambil DbContextOptions non-generik. Contohnya:
public abstract class ApplicationDbContextBase : DbContext
{
protected ApplicationDbContextBase(DbContextOptions contextOptions)
: base(contextOptions)
{
}
}
Ini memungkinkan beberapa subkelas konkret untuk memanggil konstruktor dasar ini menggunakan instans generik DbContextOptions<TContext> mereka yang berbeda. Contohnya:
public sealed class ApplicationDbContext1 : ApplicationDbContextBase
{
public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
: base(contextOptions)
{
}
}
public sealed class ApplicationDbContext2 : ApplicationDbContextBase
{
public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
: base(contextOptions)
{
}
}
Perhatikan bahwa ini adalah pola yang persis sama seperti ketika mewarisi dari DbContext secara langsung. Artinya, konstruktor DbContext itu sendiri menerima DbContextOptions non-generik karena alasan ini.
Subkelas DbContext yang dimaksudkan untuk dipakai dan diwarisi dari harus mengekspos kedua bentuk konstruktor. Contohnya:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> contextOptions)
: base(contextOptions)
{
}
protected ApplicationDbContext(DbContextOptions contextOptions)
: base(contextOptions)
{
}
}
Konfigurasi DbContext waktu desain
Alat waktu desain EF Core seperti untuk Migrasi EF Core harus dapat menemukan dan membuat instans kerja jenis DbContext untuk mengumpulkan detail tentang jenis entitas aplikasi dan bagaimana mereka memetakan ke skema database. Proses ini dapat otomatis selama alat dapat dengan mudah membuat DbContext sewaktu-waktu sehingga akan dikonfigurasi mirip dengan cara yang akan dikonfigurasi pada run-time.
Meskipun pola apa pun yang menyediakan informasi konfigurasi yang diperlukan untuk DbContext dapat berfungsi pada run-time, alat yang memerlukan penggunaan DbContext pada waktu desain hanya dapat berfungsi dengan sejumlah pola yang terbatas. Ini dibahas secara lebih rinci dalam Pembuatan Konteks Waktu Desain.
Menghindari masalah utas DbContext
Entity Framework Core tidak mendukung beberapa operasi paralel yang dijalankan pada instans DbContext yang sama. Ini termasuk eksekusi paralel kueri asinkron dan penggunaan bersamaan eksplisit dari beberapa utas. Oleh karena itu, selalu await panggilan asinkron segera, atau gunakan instans DbContext terpisah untuk operasi yang dijalankan secara paralel.
Saat EF Core mendeteksi upaya untuk menggunakan instans DbContext secara bersamaan, Anda akan melihat InvalidOperationException dengan pesan seperti ini:
Operasi kedua dimulai pada konteks ini sebelum operasi sebelumnya selesai. Hal ini biasanya disebabkan oleh utas yang berbeda menggunakan instans DbContext yang sama, namun anggota instans tidak dijamin aman untuk utas.
Ketika akses bersamaan tidak terdeteksi, itu dapat mengakibatkan perilaku yang tidak terdefinisi, crash aplikasi, dan kerusakan data.
Ada kesalahan umum yang secara tidak sengaja dapat menyebabkan akses bersamaan pada instans DbContext yang sama:
Jebakan operasi asinkron
Metode asinkron memungkinkan EF Core memulai operasi yang mengakses database dengan cara non-pemblokiran. Namun jika pemanggil tidak menunggu penyelesaian salah satu metode ini, dan melanjutkan untuk melakukan operasi lain pada DbContext, status DbContext dapat, (dan kemungkinan besar akan) rusak.
Selalu tunggu metode asinkron EF Core segera.
Berbagi instans DbContext secara implisit melalui injeksi dependensi
Metode ekstensi AddDbContext mendaftarkan DbContext jenis dengan masa pakai tercakup secara default.
Ini aman dari masalah akses bersamaan di sebagian besar aplikasi ASP.NET Core karena hanya ada satu utas yang mengeksekusi setiap permintaan klien pada waktu tertentu, dan karena setiap permintaan mendapat cakupan injeksi dependensi terpisah (dan oleh karena itu instans DbContext terpisah). Untuk model hosting Blazor Server, satu permintaan logis digunakan untuk mempertahankan sirkuit pengguna Blazor, dan dengan demikian hanya satu instans DbContext tercakup yang tersedia per sirkuit pengguna jika cakupan injeksi default digunakan.
Kode apa pun yang secara eksplisit menjalankan beberapa utas secara paralel harus memastikan bahwa instans DbContext tidak pernah diakses secara bersamaan.
Menggunakan injeksi ketergantungan, ini dapat dicapai dengan mendaftarkan konteks sebagai cakupan, dan membuat cakupan (menggunakan IServiceScopeFactory) untuk setiap utas, atau dengan mendaftarkan DbContext sebagai sementara (menggunakan kelebihan AddDbContext yang membutuhkan parameter ServiceLifetime).
ConfigureDbContext untuk komposisi konfigurasi
Catatan
Bagian ini mencakup penggunaan tingkat menengah EF Core terutama ditujukan untuk pustaka dan komponen yang dapat digunakan kembali. Sebagian besar aplikasi harus menggunakan pola yang AddDbContextFactory dijelaskan sebelumnya dalam artikel ini.
Dimulai dengan EF Core 9.0, Anda dapat menggunakan ConfigureDbContext untuk menerapkan konfigurasi tambahan ke DbContext sebelum atau sesudah AddDbContext panggilan. Ini sangat berguna untuk menyusun konfigurasi yang tidak bertentangan dalam komponen atau pengujian yang dapat digunakan kembali.
Penggunaan Dasar ConfigureDbContext
ConfigureDbContext memungkinkan Anda menambahkan konfigurasi di pustaka atau komponen yang dapat digunakan kembali tanpa mengganti seluruh konfigurasi penyedia:
var services = new ServiceCollection();
services.ConfigureDbContext<BlogContext>(options =>
options.EnableSensitiveDataLogging()
.EnableDetailedErrors());
services.AddDbContext<BlogContext>(options =>
options.UseInMemoryDatabase("BasicExample"));
Pengaturan spesifik penyedia tanpa string koneksi
Untuk menerapkan konfigurasi khusus penyedia, Anda dapat menggunakan metode konfigurasi khusus penyedia tanpa menyediakan string koneksi. Penyedia SQL Server juga menyertakan ConfigureSqlEngine untuk kasus ini. Lihat Perilaku batching khusus SQL Server untuk informasi selengkapnya.
var services = new ServiceCollection();
services.ConfigureDbContext<BlogContext>(options =>
options.UseSqlServer(sqlOptions =>
sqlOptions.EnableRetryOnFailure()));
services.AddDbContext<BlogContext>(options =>
options.UseSqlServer("connectionString"));
Prioritas ConfigureDbContext dan AddDbContext
Ketika ConfigureDbContext dan AddDbContext digunakan, atau ketika beberapa panggilan fungsi ke metode ini dilakukan, konfigurasi diterapkan dalam urutan metode dipanggil, dengan panggilan selanjutnya diutamakan untuk opsi yang bertentangan.
Untuk opsi yang tidak bertentangan (seperti menambahkan logging, interceptor, atau pengaturan lainnya), semua konfigurasi digabungkan bersama-sama.
var services = new ServiceCollection();
services.ConfigureDbContext<BlogContext>(options =>
options.LogTo(Console.WriteLine));
services.AddDbContext<BlogContext>(options =>
options.UseInMemoryDatabase("CompositionExample"));
services.ConfigureDbContext<BlogContext>(options =>
options.EnableSensitiveDataLogging());
Untuk opsi yang bertentangan, konfigurasi terakhir menang. Lihat perubahan mencolok di EF Core 8.0 untuk informasi selengkapnya tentang perubahan perilaku ini.
Catatan
Mengonfigurasi penyedia lain tidak akan menghapus konfigurasi penyedia sebelumnya. Ini dapat menyebabkan kesalahan saat membuat konteks. Untuk mengganti penyedia sepenuhnya, Anda perlu menghapus pendaftaran konteks dan menambahkannya kembali, atau membuat koleksi layanan baru.
Bacaan lainnya
- Baca Injeksi Dependensi untuk mempelajari selengkapnya tentang menggunakan DI.
- Baca Pengujian untuk informasi selengkapnya.