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.
Dalam artikel ini, Anda akan membuat aplikasi konsol .NET yang secara manual membuat ServiceCollection dan ServiceProvider yang sesuai. Anda mempelajari cara mendaftarkan layanan dan mengatasinya menggunakan injeksi dependensi (DI). Artikel ini menggunakan paket NuGet Microsoft.Extensions.DependencyInjection untuk menunjukkan dasar-dasar DI di .NET.
Catatan
Artikel ini tidak memanfaatkan fitur Host Generik. Untuk panduan yang lebih komprehensif, lihat Menggunakan injeksi dependensi di .NET.
Mulai Sekarang
Untuk memulai, buat aplikasi konsol .NET baru bernama DI.Basics. Beberapa pendekatan paling umum untuk membuat proyek konsol dirujuk dalam daftar berikut:
- Visual Studio: Menu File > Proyek Baru>.
- Visual Studio Code dan opsi menu ekstensi C# Dev Kit: Penjelajah Solusi.
-
.NET CLI:
dotnet new console
perintah di terminal.
Anda perlu menambahkan referensi paket ke Microsoft.Extensions.DependencyInjection dalam file proyek. Terlepas dari pendekatannya, pastikan proyek menyerupai XML dari file DI.Basics.csproj berikut:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
</ItemGroup>
</Project>
Dasar-dasar injeksi dependensi
Injeksi dependensi adalah pola desain yang memungkinkan Anda menghapus dependensi yang dikodekan secara permanen dan membuat aplikasi Anda lebih dapat dipertahankan dan dapat diuji. DI adalah teknik untuk mencapai Inversion of Control (IoC) antara kelas dan dependensinya.
Abstraksi untuk DI di .NET ditentukan dalam paket NuGet Microsoft.Extensions.DependencyInjection.Abstractions :
- IServiceCollection: Mendefinisikan kontrak untuk kumpulan deskriptor layanan.
- IServiceProvider: Mendefinisikan mekanisme untuk mengambil objek layanan.
- ServiceDescriptor: Menjelaskan layanan dengan jenis layanan, implementasi, dan masa pakainya.
Di .NET, DI dikelola dengan menambahkan layanan dan mengonfigurasinya dalam IServiceCollection
. Setelah layanan didaftarkan, instans IServiceProvider
dibangun dengan memanggil metode BuildServiceProvider.
IServiceProvider
berfungsi sebagai kontainer dari semua layanan yang terdaftar, dan digunakan untuk menyelesaikan pemanggilan layanan.
Membuat contoh layanan
Tidak semua layanan memiliki kualitas yang sama. Beberapa layanan memerlukan instans baru setiap kali kontainer layanan mendapatkannya (transien), sementara yang lain harus dibagikan di seluruh permintaan (lingkup) atau selama seluruh masa pakai aplikasi (singleton). Untuk informasi selengkapnya tentang masa pakai layanan, lihat Masa pakai layanan.
Demikian juga, beberapa layanan hanya mengekspos tipe konkret, sementara yang lain diekspresikan sebagai kontrak antara antarmuka dan tipe implementasi. Anda membuat beberapa variasi layanan untuk membantu menunjukkan konsep-konsep ini.
Buat file C# baru bernama IConsole.cs dan tambahkan kode berikut:
public interface IConsole
{
void WriteLine(string message);
}
File ini mendefinisikan IConsole
antarmuka yang mengekspos satu metode, WriteLine
. Selanjutnya, buat file C# baru bernama DefaultConsole.cs dan tambahkan kode berikut:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
Kode sebelumnya mewakili implementasi IConsole
default antarmuka. Metode WriteLine
menulis ke konsol secara kondisional berdasarkan properti IsEnabled
.
Tips
Penamaan implementasi adalah pilihan yang harus disepakati oleh tim pengembangan Anda. Awalan Default
adalah konvensi umum untuk menunjukkan implementasi default antarmuka, tetapi tidak harus digunakan.
Selanjutnya, buat file IGreetingService.cs dan tambahkan kode C# berikut:
public interface IGreetingService
{
string Greet(string name);
}
Kemudian tambahkan file C# baru bernama DefaultGreetingService.cs dan tambahkan kode berikut:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
Kode sebelumnya mewakili implementasi IGreetingService
default antarmuka. Implementasi layanan memerlukan IConsole
sebagai parameter konstruktor utama. Metode Greet
:
- Membuat
greeting
berdasarkanname
. - Memanggil metode
WriteLine
pada instansIConsole
. - Mengembalikan
greeting
kepada pemanggil.
Layanan terakhir yang dibuat adalah file FarewellService.cs , tambahkan kode C# berikut sebelum melanjutkan:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
Ini mewakili FarewellService
jenis konkret, bukan antarmuka. Ini harus dinyatakan public
agar dapat diakses oleh konsumen. Tidak seperti jenis implementasi layanan lain yang dideklarasikan sebagai internal
dan sealed
, kode ini menunjukkan bahwa tidak semua layanan perlu menjadi antarmuka. Ini juga menunjukkan bahwa implementasi layanan dapat sealed
untuk mencegah pewarisan dan internal
untuk membatasi akses ke perangkat rakitan.
Memperbarui kelas Program
Buka file Program.cs dan ganti kode yang ada dengan kode C# berikut:
using Microsoft.Extensions.DependencyInjection;
// 1. Create the service collection.
var services = new ServiceCollection();
// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
});
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();
// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();
// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();
// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");
Kode yang diperbarui sebelumnya menunjukkan cara:
- Buat instans
ServiceCollection
baru. - Mendaftarkan dan mengonfigurasi layanan di
ServiceCollection
:-
IConsole
menggunakan kelebihan beban pabrik implementasi, mengembalikan jenisDefaultConsole
denganIsEnabled
diatur ketrue
. -
IGreetingService
ditambahkan dengan tipe implementasi yang sesuai dari jenisDefaultGreetingService
. -
FarewellService
ditambahkan sebagai tipe konkret.
-
-
ServiceProvider
Buat dariServiceCollection
. - Selesaikan layanan
IGreetingService
danFarewellService
. - Gunakan layanan yang tersedia untuk menyambut dan mengucapkan selamat tinggal kepada orang yang bernama
David
.
Jika Anda memperbarui properti IsEnabled
dari DefaultConsole
ke false
, metode Greet
dan SayGoodbye
menghilangkan penulisan ke pesan yang dihasilkan ke konsol. Perubahan semacam ini, membantu menunjukkan bahwa IConsole
layanan disuntikkan ke dalam IGreetingService
layanan dan FarewellService
layanan sebagai dependensi yang memengaruhi perilaku aplikasi tersebut.
Semua layanan ini terdaftar sebagai singleton, meskipun untuk sampel ini, layanan ini bekerja secara identik jika terdaftar sebagai sementara atau layanan cakupan.
Penting
Dalam contoh buatan ini, masa pakai layanan tidak menjadi masalah, tetapi dalam aplikasi dunia nyata, Anda sebaiknya mempertimbangkan dengan cermat masa pakai setiap layanan.
Menjalankan contoh aplikasi
Untuk menjalankan aplikasi sampel, tekan F5 di Visual Studio, Visual Studio Code, atau jalankan dotnet run
perintah di terminal. Setelah aplikasi selesai, Anda akan melihat output berikut:
Hello, David!
Goodbye, David!
Deskriptor layanan
API yang paling umum digunakan untuk menambahkan layanan ke ServiceCollection
adalah metode ekstensi generik yang diberi nama "lifetime", seperti:
AddSingleton<TService>
AddTransient<TService>
AddScoped<TService>
Metode-metode ini adalah metode bantu yang membuat ServiceDescriptor instance dan menambahkannya ke ServiceCollection
.
ServiceDescriptor
adalah kelas sederhana yang menjelaskan layanan dengan jenis layanan, jenis implementasi, dan masa pakainya. Ini juga dapat menjelaskan pabrik dan instans implementasi.
Untuk setiap layanan yang Anda daftarkan di ServiceCollection
, Anda bisa memanggil metode Add
dengan instans ServiceDescriptor
secara langsung. Perhatikan contoh berikut:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
Kode sebelumnya setara dengan cara layanan IConsole
terdaftar di ServiceCollection
. Metode Add
ini digunakan untuk menambahkan instans ServiceDescriptor
yang menjelaskan layanan IConsole
. Metode statis ServiceDescriptor.Describe
mendelegasikan ke berbagai ServiceDescriptor
konstruktor. Pertimbangkan kode yang setara untuk IGreetingService
layanan:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
Kode sebelumnya menjelaskan IGreetingService
layanan dengan jenis layanan, jenis implementasi, dan masa pakainya. Terakhir, pertimbangkan kode yang setara untuk layanan FarewellService
.
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
Kode sebelumnya menjelaskan jenis konkret FarewellService
sebagai jenis layanan dan implementasi. Layanan ini terdaftar sebagai layanan singleton.