Bagikan melalui


Memahami dasar-dasar injeksi dependensi di .NET

Dalam artikel ini, Anda membuat aplikasi konsol .NET yang secara manual membuat ServiceCollection dan sesuai ServiceProvider. 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.

Memulai

Untuk memulai, buat aplikasi konsol .NET baru bernama DI.Basics. Beberapa pendekatan paling umum untuk membuat proyek konsol dirujuk dalam daftar berikut:

Anda perlu menambahkan referensi paket ke Microsoft.Extensions.DependencyInjection dalam file proyek. Terlepas dari pendekatannya, pastikan proyek menyerupan XML berikut dari file DI.Basics.csproj :

<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.3" />
  </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 :

Di .NET, DI dikelola dengan menambahkan layanan dan mengonfigurasinya dalam IServiceCollection. Setelah layanan didaftarkan, IServiceProvider instans dibangun dengan memanggil BuildServiceProvider metode . Bertindak IServiceProvider sebagai kontainer dari semua layanan terdaftar, dan digunakan untuk menyelesaikan layanan.

Membuat contoh layanan

Tidak semua layanan dibuat secara merata. 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 jenis beton, sementara yang lain dinyatakan sebagai kontrak antara antarmuka dan jenis 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 ini WriteLine secara kondisional menulis ke konsol berdasarkan IsEnabled properti .

Tip

Penamaan implementasi adalah pilihan yang harus disepakati oleh tim pengembangan Anda. Awalan Default adalah konvensi umum untuk menunjukkan implementasi default antarmuka, tetapi tidak diperlukan.

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:

  • greeting nameMembuat .
  • Memanggil metode pada WriteLine instansIConsole.
  • Mengembalikan ke greeting 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;
    }
}

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 warisan dan internal membatasi akses ke perakitan.

Program Memperbarui kelas

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:

  • Membuat contoh ServiceCollection instans.
  • Mendaftarkan dan mengonfigurasi layanan di ServiceCollection:
    • IConsole menggunakan kelebihan beban pabrik implementasi, mengembalikan jenis DefaultConsole dengan IsEnabled diatur ke true.
    • IGreetingService ditambahkan dengan jenis implementasi yang DefaultGreetingService sesuai.
    • FarewellService ditambahkan sebagai jenis beton.
  • ServiceProvider Buat dari ServiceCollection.
  • Atasi IGreetingService layanan dan FarewellService .
  • Gunakan layanan yang diselesaikan untuk menyambut dan mengucapkan selamat tinggal kepada orang bernama David.

Jika Anda memperbarui IsEnabled properti ke DefaultConsolefalse, Greet metode dan SayGoodbye menghilangkan penulisan ke pesan yang dihasilkan ke konsol. Perubahan seperti ini, membantu menunjukkan bahwa IConsole layanan disuntikkan ke dalam IGreetingService layanan dan FarewellService 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 metode ekstensi generik bernama seumur hidup, seperti:

  • AddSingleton<TService>
  • AddTransient<TService>
  • AddScoped<TService>

Metode ini adalah metode kenyamanan yang membuat ServiceDescriptor instans 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 dapat memanggil Add metode dengan instans secara ServiceDescriptor langsung. Perhatikan contoh berikut:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));

Kode sebelumnya setara dengan bagaimana IConsole layanan terdaftar di ServiceCollection. Metode Add ini digunakan untuk menambahkan ServiceDescriptor instans IConsole yang menjelaskan layanan. 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 FarewellService layanan:

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.

Lihat juga