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.
Tutorial ini menunjukkan cara menggunakan injeksi dependensi (DI) di .NET. Dengan Microsoft Extensions, DI dikelola dengan menambahkan layanan dan mengonfigurasinya dalam IServiceCollection. Antarmuka IHost mengekspos instans IServiceProvider, yang bertindak sebagai kontainer dari semua layanan terdaftar.
Dalam tutorial ini, Anda mempelajari cara:
- Membuat aplikasi konsol .NET yang menggunakan injeksi dependensi
- Membuat dan mengonfigurasi Host Generik
- Menulis beberapa antarmuka dan implementasi yang sesuai
- Gunakan masa pakai layanan dan cakupan untuk DI
Prasyarat
- .NET Core 3.1 SDK atau yang lebih baru.
- Keakraban dengan membuat aplikasi .NET baru dan menginstal paket NuGet.
Membuat aplikasi konsol baru
Menggunakan perintah dotnet new atau wizard proyek baru di IDE, buat aplikasi konsol .NET baru bernama ConsoleDI.Example. Tambahkan paket Microsoft.Extensions.Hosting NuGet ke proyek.
File proyek aplikasi konsol baru Anda harus menyerupai yang berikut ini:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ConsoleDI.Example</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
</ItemGroup>
</Project>
Penting
Dalam contoh ini, paket Microsoft.Extensions.Hosting NuGet diperlukan untuk membangun dan menjalankan aplikasi. Beberapa metapackage mungkin berisi paket Microsoft.Extensions.Hosting, dalam hal ini referensi paket eksplisit tidak diperlukan.
Menambahkan antarmuka
Dalam aplikasi sampel ini, Anda mempelajari bagaimana injeksi dependensi menangani masa pakai layanan. Anda membuat beberapa antarmuka yang mewakili masa pakai layanan yang berbeda. Tambahkan antarmuka berikut ke direktori akar proyek:
IReportServiceLifetime.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IReportServiceLifetime
{
Guid Id { get; }
ServiceLifetime Lifetime { get; }
}
Antarmuka IReportServiceLifetime mendefinisikan:
- Properti
Guid Idyang mewakili pengidentifikasi unik layanan. - Properti ServiceLifetime yang mewakili masa pakai layanan.
IExampleTransientService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleTransientService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}
IExampleScopedService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleScopedService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}
IExampleSingletonService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleSingletonService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}
Semua subinterface IReportServiceLifetime secara eksplisit mengimplementasikan IReportServiceLifetime.Lifetime dengan default. Misalnya, IExampleTransientService secara eksplisit menerapkan IReportServiceLifetime.Lifetime dengan nilai ServiceLifetime.Transient.
Tambahkan implementasi default
Contoh implementasi semua menginisialisasi properti mereka Id dengan hasil Guid.NewGuid(). Tambahkan kelas implementasi default berikut untuk berbagai layanan ke direktori akar proyek:
ExampleTransientService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleTransientService : IExampleTransientService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleScopedService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleScopedService : IExampleScopedService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleSingletonService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleSingletonService : IExampleSingletonService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
Setiap implementasi didefinisikan sebagai internal sealed dan mengimplementasikan antarmuka yang sesuai. Mereka tidak diwajibkan menjadi internal atau sealed, namun biasa memperlakukan implementasi sebagai internal untuk menghindari kebocoran jenis implementasi kepada pihak luar. Selain itu, karena setiap jenis tidak diperluas, itu ditandai sebagai sealed. Misalnya, ExampleSingletonService menerapkan IExampleSingletonService.
Menambahkan layanan yang memerlukan DI
Tambahkan kelas pelapor masa pakai layanan berikut, yang berfungsi sebagai layanan untuk aplikasi konsol:
ServiceLifetimeReporter.cs
namespace ConsoleDI.Example;
internal sealed class ServiceLifetimeReporter(
IExampleTransientService transientService,
IExampleScopedService scopedService,
IExampleSingletonService singletonService)
{
public void ReportServiceLifetimeDetails(string lifetimeDetails)
{
Console.WriteLine(lifetimeDetails);
LogService(transientService, "Always different");
LogService(scopedService, "Changes only with lifetime");
LogService(singletonService, "Always the same");
}
private static void LogService<T>(T service, string message)
where T : IReportServiceLifetime =>
Console.WriteLine(
$" {typeof(T).Name}: {service.Id} ({message})");
}
ServiceLifetimeReporter mendefinisikan konstruktor yang memerlukan setiap antarmuka layanan yang disebutkan di atas, yaitu, IExampleTransientService, IExampleScopedService, dan IExampleSingletonService. Objek mengekspos satu metode yang memungkinkan konsumen melaporkan layanan dengan parameter lifetimeDetails tertentu. Saat dipanggil, metode ReportServiceLifetimeDetails mencatat pengidentifikasi unik setiap layanan dengan pesan durasi layanan. Pesan log membantu memvisualisasikan masa pakai layanan.
Mendaftarkan layanan untuk DI
Perbarui Program.cs dengan kode berikut:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();
using IHost host = builder.Build();
ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");
await host.RunAsync();
static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
using IServiceScope serviceScope = hostProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine("...");
logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine();
}
Setiap metode ekstensi services.Add{LIFETIME}<{SERVICE}> menambahkan layanan (dan berpotensi mengonfigurasi). Sebaiknya aplikasi mengikuti konvensi ini. Jangan menempatkan metode ekstensi di namespace Microsoft.Extensions.DependencyInjection kecuali Anda menulis paket Microsoft resmi. Metode ekstensi yang ditentukan dalam namespace Microsoft.Extensions.DependencyInjection:
- Ditampilkan dalam IntelliSense tanpa memerlukan direktif tambahan.
- Kurangi jumlah arahan
usingyang diperlukan di kelasProgramatauStartuptempat metode ekstensi ini biasanya dipanggil.
Aplikasi:
- Membuat instans IHostApplicationBuilder dengan pengaturan pembuat host.
- Mengonfigurasi layanan dan menambahkannya dengan masa pakai layanan yang sesuai.
- Memanggil Build() dan menetapkan sebuah instans dari IHost.
- Memanggil
ExemplifyServiceLifetime, dengan meneruskan IHost.Services.
Kesimpulan
Dalam aplikasi sampel ini, Anda membuat beberapa antarmuka dan implementasi yang sesuai. Masing-masing layanan ini diidentifikasi secara unik dan dipasangkan dengan ServiceLifetime. Aplikasi sampel menunjukkan bagaimana mendaftarkan implementasi layanan pada antarmuka, dan cara mendaftarkan kelas murni tanpa antarmuka pendukung. Aplikasi sampel kemudian menunjukkan bagaimana dependensi yang didefinisikan sebagai parameter konstruktor diselesaikan pada runtime.
Saat Anda menjalankan aplikasi, aplikasi akan menampilkan output yang mirip dengan yang berikut ini:
// Sample output:
// Lifetime 1: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 1: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
//
// Lifetime 2: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 2: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
Dari output aplikasi, Anda dapat melihat bahwa:
- Transient layanan selalu berbeda. Instans baru dibuat setiap kali layanan diakses.
- Scoped layanan hanya berubah ketika ada cakupan yang baru, tetapi tetap menjadi instans yang sama dalam satu cakupan.
- Singleton layanan selalu sama. Instans baru hanya dibuat sekali.