Bagikan melalui


Mulai cepat: Dasar-dasar injeksi dependensi di .NET

Dalam panduan memulai cepat ini, Anda membuat aplikasi konsol .NET yang secara manual membuat ServiceCollection dan pasangan ServiceProvider. Anda mempelajari cara mendaftarkan layanan dan mengatasinya dengan menggunakan injeksi dependensi (DI). Artikel ini menggunakan paket NuGet Microsoft.Extensions.DependencyInjection untuk menunjukkan dasar-dasar DI di .NET.

Nota

Artikel ini tidak memanfaatkan fitur host Generik . Untuk panduan yang lebih komprehensif, lihat Menggunakan injeksi dependensi di .NET.

Get started

Untuk memulai, buat aplikasi konsol .NET baru bernama DI.Basics. Di Visual Studio, pilih File > Proyek Baru>, atau menggunakan .NET CLI, masukkan dotnet new console.

Selanjutnya, tambahkan referensi paket ke Microsoft.Extensions.DependencyInjection dalam file proyek. Setelah Anda menambahkan paket, pastikan proyek menyerupai XML berikut dari file DI.Basics.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
  </ItemGroup>

</Project>

Dasar-dasar injeksi dependensi

Injeksi dependensi adalah pola desain yang dapat Anda gunakan untuk 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.

Paket NuGet Microsoft.Extensions.DependencyInjection.Abstractions menentukan abstraksi untuk DI di .NET:

Di .NET, Anda mengelola DI dengan menambahkan layanan dan mengonfigurasinya dalam IServiceCollection. Setelah Anda mendaftarkan layanan, panggil metode BuildServiceProvider untuk membangun instance IServiceProvider. Bertindak IServiceProvider sebagai kontainer untuk semua layanan terdaftar, dan Anda menggunakannya untuk menyelesaikan 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 (berlingkup) 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 konkret, sedangkan yang lain diekspresikan 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 WriteLine ini secara kondisional menulis ke konsol berdasarkan properti IsEnabled.

Petunjuk / Saran

Penamaan implementasi adalah pilihan yang harus disepakati oleh tim pengembang 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:

  • Membuat greeting diberikan name.
  • Memanggil metode WriteLine pada instans IConsole.
  • Mengembalikan greeting kepada pemanggil.

Kelas DefaultGreetingService menunjukkan bahwa Anda dapat seal mengimplementasikan layanan untuk mencegah pewarisan dan menggunakan internal untuk membatasi akses ke assembly.

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 FarewellService mewakili jenis konkret, bukan antarmuka. Anda harus menyatakannya public agar dapat diakses oleh konsumen. Tidak seperti jenis implementasi layanan lain yang Anda nyatakan sebagai internal dan sealed, kode ini menunjukkan bahwa tidak semua layanan perlu menjadi antarmuka.

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 sebelumnya menunjukkan cara:

  • Buat instans baru ServiceCollection .
  • Mendaftarkan dan mengonfigurasi layanan di ServiceCollection:
    • Layanan IConsole dengan menggunakan pabrik implementasi kelebihan beban. Kembalikan jenis DefaultConsole dengan properti IsEnabled yang diatur ke true.
    • Layanan IGreetingService dengan jenis implementasi yang sesuai dari DefaultGreetingService.
    • Layanan FarewellService sebagai tipe konkret.
  • ServiceProvider Buat dari ServiceCollection.
  • Selesaikan layanan IGreetingService dan FarewellService.
  • Gunakan layanan yang telah diselesaikan untuk menyambut dan mengucapkan selamat tinggal kepada orang bernama David.

Jika Anda memperbarui IsEnabled properti ke DefaultConsolefalse, Greet metode dan SayGoodbye menghilangkan penulisan pesan yang dihasilkan ke konsol. Perubahan ini membantu menunjukkan bahwa layanan IConsoledisuntikkan dalam bentuk IGreetingService dan FarewellService sebagai dependensi yang memengaruhi perilaku aplikasi.

Semua layanan ini terdaftar sebagai singleton. Untuk sampel ini, ini berfungsi secara identik jika Anda mendaftarkannya sebagai layanan sementara atau terlingkup .

Penting

Dalam contoh yang diusung ini, masa pakai layanan tidak masalah. Dalam aplikasi dunia nyata, pertimbangkan dengan cermat masa pakai setiap layanan.

Menjalankan aplikasi sampel

Untuk menjalankan aplikasi sampel, tekan F5 di Visual Studio atau Visual Studio Code, atau jalankan dotnet run perintah di terminal. Saat 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 bernama "lifetime", seperti:

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

Metode ini adalah metode kemudahan 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 dapat 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 didaftarkan di ServiceCollection. Metode ini menambahkan instans ServiceDescriptor yang menggambarkan 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 didaftarkan sebagai layanan tunggal.

Lihat juga