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.
.NET mendukung pola desain perangkat lunak injeksi dependensi (DI), yang merupakan teknik untuk mencapai Inversion of Control (IoC) antara kelas dan dependensinya. Injeksi dependensi di .NET adalah bagian bawaan dari kerangka kerja, bersama dengan konfigurasi, pengelogan, dan pola opsi.
Dependensi adalah objek yang bergantung pada objek lain. Periksa kelas berikut MessageWriter dengan Write metode yang bergantung pada kelas lain:
public class MessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Sebuah kelas dapat membuat instance dari kelas MessageWriter untuk menggunakan metode Write miliknya. Dalam contoh berikut, MessageWriter kelas adalah dependensi dari Worker kelas :
public class Worker : BackgroundService
{
private readonly MessageWriter _messageWriter = new();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
Kelas membuat dan secara langsung tergantung pada MessageWriter kelas . Dependensi yang dikodekan secara permanen, seperti dalam contoh sebelumnya, bermasalah dan harus dihindari karena alasan berikut:
- Untuk mengganti
MessageWriterdengan implementasi yang berbeda, Anda harus memodifikasiWorkerkelas. - Jika
MessageWritermemiliki dependensi,Workerkelas juga harus mengonfigurasinya. Dalam proyek besar dengan beberapa kelas tergantung padaMessageWriter, kode konfigurasi menjadi tersebar di seluruh aplikasi. - Implementasi ini sulit untuk pengujian unit. Aplikasi harus menggunakan kelas tiruan atau stub
MessageWriter, yang tidak dimungkinkan dengan pendekatan ini.
Injeksi dependensi mengatasi masalah berikut melalui:
- Penggunaan antarmuka atau kelas dasar untuk mengabstraksi implementasi dependensi.
- Pendaftaran dependensi dalam kontainer layanan. .NET menyediakan kontainer layanan bawaan, IServiceProvider. Layanan biasanya terdaftar di start-up aplikasi dan ditambahkan ke IServiceCollection. Setelah semua layanan ditambahkan, gunakan BuildServiceProvider untuk membuat kontainer layanan.
- Injeksi layanan ke konstruktor kelas tempat layanan digunakan. Kerangka kerja mengambil tanggung jawab untuk membuat instans dependensi dan membuangnya ketika tidak lagi diperlukan.
Sebagai contoh, IMessageWriter antarmuka mendefinisikan Write metode :
namespace DependencyInjection.Example;
public interface IMessageWriter
{
void Write(string message);
}
Antarmuka ini diimplementasikan oleh jenis beton, MessageWriter:
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Kode sampel mendaftarkan IMessageWriter layanan dengan jenis MessageWriterbeton . Metode ini AddSingleton mendaftarkan layanan dengan masa pakai singleton, masa pakai aplikasi.
Masa pakai layanan dijelaskan nanti dalam artikel ini.
using DependencyInjection.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
Dalam kode sebelumnya, aplikasi sampel:
Membuat instans pembuat aplikasi host.
Mengonfigurasi layanan dengan mendaftarkan:
-
Workersebagai layanan yang dihosting. Untuk informasi selengkapnya, lihat Layanan Pekerja di .NET. - Antarmuka
IMessageWritersebagai layanan singleton dengan implementasi kelas yangMessageWritersesuai.
-
Membangun host dan menjalankannya.
Host berisi penyedia layanan injeksi dependensi. Ini juga berisi semua layanan relevan lainnya yang diperlukan untuk secara otomatis membuat Worker instans dan memberikan implementasi yang sesuai IMessageWriter sebagai argumen.
namespace DependencyInjection.Example;
public sealed class Worker(IMessageWriter messageWriter) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
Dengan menggunakan pola DI, layanan pekerja:
- Tidak menggunakan jenis
MessageWriterbeton , hanyaIMessageWriterantarmuka yang diimplementasikannya. Ini memudahkan untuk mengganti implementasi yang digunakan oleh layanan pekerja tanpa harus memodifikasi layanan tersebut. - Tidak membuat instans .
MessageWriterKontainer DI membuat instance.
Implementasi IMessageWriter antarmuka dapat ditingkatkan menggunakan API pengelogan bawaan:
namespace DependencyInjection.Example;
public class LoggingMessageWriter(
ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}
Metode yang diperbarui AddSingleton mendaftarkan implementasi baru IMessageWriter :
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
Jenis HostApplicationBuilder (builder) adalah bagian Microsoft.Extensions.Hosting dari paket NuGet.
LoggingMessageWriter tergantung pada ILogger<TCategoryName>, yang dimintanya di konstruktor.
ILogger<TCategoryName>adalah layanan yang disediakan kerangka kerja.
Tidak biasa menggunakan injeksi dependensi dengan cara berantai. Setiap dependensi yang diminta pada gilirannya meminta dependensinya sendiri. Kontainer menyelesaikan dependensi dalam grafik dan mengembalikan layanan yang diselesaikan sepenuhnya. Kumpulan dependensi kolektif yang harus diselesaikan biasanya disebut pohon dependensi, grafik dependensi, atau grafik objek.
Kontainer menyelesaikan ILogger<TCategoryName> dengan memanfaatkan jenis terbuka (generik), menghilangkan kebutuhan untuk mendaftarkan setiap jenis yang dibangun (generik).
Dengan terminologi injeksi dependensi, layanan:
- Biasanya merupakan objek yang menyediakan layanan ke objek lain, seperti
IMessageWriterlayanan. - Tidak terkait dengan layanan web, meskipun layanan mungkin menggunakan layanan web.
Kerangka kerja ini menyediakan sistem pengelogan yang kuat. Implementasi IMessageWriter yang ditunjukkan dalam contoh sebelumnya menunjukkan DI dasar, bukan pengelogan. Sebagian besar aplikasi tidak perlu menulis pencatat. Kode berikut menunjukkan menggunakan pengelogan default, yang hanya mengharuskan Worker didaftarkan sebagai layanan AddHostedServiceyang dihosting :
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}
Menggunakan kode sebelumnya, tidak perlu memperbarui Program.cs, karena kerangka kerja menyediakan pengelogan.
Beberapa aturan penemuan konstruktor
Ketika jenis mendefinisikan lebih dari satu konstruktor, penyedia layanan memiliki logika untuk menentukan konstruktor mana yang akan digunakan. Konstruktor dengan parameter terbanyak di mana jenis dapat diselesaikan dipilih. Pertimbangkan layanan contoh C# berikut:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(FooService fooService, BarService barService)
{
// omitted for brevity
}
}
Dalam kode sebelumnya, asumsikan bahwa pengelogan telah ditambahkan dan dapat diselesaikan dari penyedia layanan tetapi FooService jenis dan BarService tidak. Konstruktor dengan ILogger<ExampleService> parameter menyelesaikan instans ExampleService . Meskipun ada konstruktor yang mendefinisikan lebih banyak parameter, FooService dan jenis BarService tidak dapat diselesaikan.
Jika ada ambiguitas saat menemukan konstruktor, pengecualian akan dilemparkan. Pertimbangkan layanan contoh C# berikut:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Peringatan
Kode ExampleService dengan parameter jenis DI-resolvable ambigu melempar pengecualian.
Jangan lakukan ini—hal ini dimaksudkan untuk menunjukkan maksud dari "jenis yang dapat diresolusikan secara ambigu dalam DI".
Dalam contoh sebelumnya, ada tiga konstruktor. Konstruktor pertama tanpa parameter dan tidak memerlukan layanan dari penyedia layanan. Asumsikan bahwa pengelogan dan opsi telah ditambahkan ke kontainer DI dan merupakan layanan yang dapat diselesaikan. Ketika kontainer DI mencoba memperbaiki jenis ExampleService, kontainer DI melempar pengecualian, karena kedua konstruktor ambigu.
Hindari ambiguitas dengan menentukan konstruktor yang menerima tipe yang dapat diselesaikan melalui DI sebagai gantinya.
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Mendaftarkan grup layanan dengan metode ekstensi
Microsoft Extensions menggunakan konvensi untuk mendaftarkan sekelompok layanan terkait. Konvensi ini menggunakan metode ekstensi tunggal Add{GROUP_NAME} untuk mendaftarkan semua layanan yang diperlukan oleh fitur kerangka kerja. Misalnya, AddOptions metode ekstensi mendaftarkan semua layanan yang diperlukan untuk menggunakan opsi.
Layanan yang disediakan kerangka kerja
Saat menggunakan salah satu pola host atau penyusun aplikasi yang tersedia, default diterapkan dan layanan didaftarkan oleh kerangka kerja. Pertimbangkan beberapa pola host dan pembuat aplikasi paling populer:
- Host.CreateDefaultBuilder()
- Host.CreateApplicationBuilder()
- WebHost.CreateDefaultBuilder()
- WebApplication.CreateBuilder()
- WebAssemblyHostBuilder.CreateDefault
- MauiApp.CreateBuilder
Setelah membuat penyusun dari salah satu API ini, IServiceCollection memiliki layanan yang ditentukan oleh kerangka kerja, tergantung pada bagaimana Anda mengonfigurasi host. Untuk aplikasi berdasarkan templat .NET, kerangka kerja dapat mendaftarkan ratusan layanan.
Tabel berikut mencantumkan sampel kecil layanan terdaftar kerangka kerja ini:
| Jenis Layanan | Seumur hidup |
|---|---|
| Microsoft.Extensions.DependencyInjection.IServiceScopeFactory | Singleton |
| IHostApplicationLifetime | Singleton |
| Microsoft.Extensions.Logging.ILogger<TCategoryName> | Singleton |
| Microsoft.Extensions.Logging.ILoggerFactory | Singleton |
| Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Singleton |
| Microsoft.Extensions.Options.IConfigureOptions<TOptions> | Perubahan sementara |
| Microsoft.Extensions.Options.IOptions<TOptions> | Singleton |
| System.Diagnostics.DiagnosticListener | Singleton |
| System.Diagnostics.DiagnosticSource | Singleton |
Masa pakai layanan
Layanan dapat didaftarkan dengan salah satu masa pakai berikut:
Bagian berikut menjelaskan masing-masing masa pakai sebelumnya. Pilih masa pakai yang sesuai untuk setiap layanan terdaftar.
Perubahan sementara
Layanan masa pakai sementara dibuat setiap kali diminta dari kontainer layanan. Untuk mendaftarkan layanan sebagai sementara, panggil AddTransient.
Di aplikasi yang memproses permintaan, layanan sementara dibuang di akhir permintaan. Masa pakai ini menimbulkan alokasi per/permintaan, karena layanan diselesaikan dan dibangun setiap saat. Untuk informasi selengkapnya, lihat Panduan Injeksi Dependensi: Panduan yang dapat diubah untuk instans sementara dan bersama.
Cakupan
Untuk aplikasi web, masa pakai terlingkup menunjukkan bahwa layanan dibuat sekali per permintaan klien (koneksi). Daftarkan layanan terlingkup dengan AddScoped.
Di aplikasi yang memproses permintaan, layanan tercakup dibuang di akhir permintaan.
Catatan
Saat menggunakan Entity Framework Core, AddDbContext metode ekstensi mendaftarkan DbContext jenis dengan masa pakai terlingkup secara default.
Layanan terlingkup harus selalu digunakan dari dalam cakupan—baik cakupan implisit (seperti cakupan per permintaan ASP.NET Core) atau cakupan eksplisit yang dibuat dengan IServiceScopeFactory.CreateScope().
Jangan menyelesaikan layanan terlingkup langsung dari singleton menggunakan injeksi konstruktor atau dengan memintanya dari IServiceProvider dalam singleton. Melakukan hal tersebut menyebabkan layanan yang dibatasi cakupannya bertindak seperti singleton, yang dapat menyebabkan keadaan yang tidak benar saat memproses permintaan berikutnya.
Dapat diterima untuk menyelesaikan layanan dengan lingkup di dalam singleton jika Anda membuat dan menggunakan cakupan eksplisit dengan IServiceScopeFactory.
Hal ini juga baik-baik saja untuk:
- Atasi layanan singleton dari layanan terlingkup atau sementara.
- Atasi layanan terlingkup dari layanan lain yang terlingkup atau sementara.
Secara default, di lingkungan pengembangan, menyelesaikan layanan dari layanan lain dengan masa pakai yang lebih lama memberikan pengecualian. Untuk informasi selengkapnya, lihat Validasi cakupan.
Singleton
Layanan seumur hidup Singleton dibuat baik:
- Pertama kali mereka diminta.
- Oleh pengembang, saat memberikan instans implementasi langsung ke kontainer. Pendekatan ini jarang diperlukan.
Setiap permintaan implementasi layanan berikutnya dari kontainer injeksi dependensi menggunakan instans yang sama. Jika aplikasi memerlukan perilaku singleton, izinkan kontainer layanan untuk mengelola masa pakai layanan. Jangan terapkan pola desain singleton dan berikan kode untuk membuang singleton. Layanan tidak boleh dibuang oleh kode yang menyelesaikan layanan dari kontainer. Jika jenis atau pabrik terdaftar sebagai singleton, kontainer akan membuang singleton secara otomatis.
Daftarkan layanan singleton dengan AddSingleton. Layanan singleton harus aman utas dan sering digunakan dalam layanan stateless.
Dalam aplikasi yang memproses permintaan, layanan singleton dibuang saat ServiceProvider dibuang pada pematian aplikasi. Karena memori tidak dibebaskan sampai aplikasi dihentikan, pertimbangkan penggunaan memori dengan layanan singleton.
Metode pendaftaran layanan
Kerangka kerja ini menyediakan metode ekstensi pendaftaran layanan yang berguna dalam skenario tertentu:
| Metode | Otomatis objek Pembuangan |
Beberapa Implementasi |
Meneruskan arg |
|---|---|---|---|
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()Contoh: services.AddSingleton<IMyDep, MyDep>(); |
Ya | Ya | Tidak |
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})Contoh: services.AddSingleton<IMyDep>(sp => new MyDep());services.AddSingleton<IMyDep>(sp => new MyDep(99)); |
Ya | Ya | Ya |
Add{LIFETIME}<{IMPLEMENTATION}>()Contoh: services.AddSingleton<MyDep>(); |
Ya | Tidak | Tidak |
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})Contoh: services.AddSingleton<IMyDep>(new MyDep());services.AddSingleton<IMyDep>(new MyDep(99)); |
Tidak | Ya | Ya |
AddSingleton(new {IMPLEMENTATION})Contoh: services.AddSingleton(new MyDep());services.AddSingleton(new MyDep(99)); |
Tidak | Tidak | Ya |
Untuk informasi selengkapnya tentang pembuangan jenis, lihat bagian Pembuangan layanan .
Mendaftarkan layanan hanya dengan jenis implementasi setara dengan mendaftarkan layanan tersebut dengan implementasi dan jenis layanan yang sama. Sebagai contoh, perhatikan kode berikut:
services.AddSingleton<ExampleService>();
Ini setara dengan mendaftarkan layanan dengan layanan dan implementasi jenis yang sama:
services.AddSingleton<ExampleService, ExampleService>();
Kesetaraan inilah sebabnya beberapa implementasi layanan tidak dapat didaftarkan menggunakan metode yang tidak mengambil jenis layanan eksplisit. Metode ini dapat mendaftarkan beberapa instans layanan, tetapi semuanya memiliki implementasi yang sama jenis.
Salah satu metode pendaftaran layanan dapat digunakan untuk mendaftarkan beberapa instans layanan dengan jenis layanan yang sama. Dalam contoh berikut, AddSingleton dipanggil dua kali dengan IMessageWriter sebagai jenis layanan. Panggilan kedua untuk AddSingleton mengambil alih yang sebelumnya ketika diselesaikan sebagai IMessageWriter dan ditambahkan ke yang sebelumnya ketika beberapa layanan diselesaikan melalui IEnumerable<IMessageWriter>. Layanan muncul dalam urutan terdaftar ketika diselesaikan melalui IEnumerable<{SERVICE}>.
using ConsoleDI.IEnumerableExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();
using IHost host = builder.Build();
_ = host.Services.GetService<ExampleService>();
await host.RunAsync();
Kode sumber sampel sebelumnya mendaftarkan dua implementasi dari IMessageWriter.
using System.Diagnostics;
namespace ConsoleDI.IEnumerableExample;
public sealed class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is LoggingMessageWriter);
var dependencyArray = messageWriters.ToArray();
Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
}
}
ExampleService menentukan dua parameter konstruktor; satu IMessageWriter, dan IEnumerable<IMessageWriter>. Tunggal IMessageWriter adalah implementasi terakhir yang didaftarkan, sedangkan IEnumerable<IMessageWriter> mewakili semua implementasi terdaftar.
Kerangka kerja ini juga menyediakan TryAdd{LIFETIME} metode ekstensi, yang mendaftarkan layanan hanya jika belum ada implementasi yang terdaftar.
Dalam contoh berikut, panggilan untuk AddSingleton mendaftar ConsoleMessageWriter sebagai implementasi untuk IMessageWriter. Panggilan ke TryAddSingleton tidak berpengaruh karena IMessageWriter sudah memiliki implementasi terdaftar:
services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();
TryAddSingleton tidak berpengaruh, karena sudah ditambahkan dan "coba" gagal. Komponen ExampleService menegaskan hal berikut:
public class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is ConsoleMessageWriter);
Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
}
}
Untuk informasi selengkapnya, lihat:
Metode TryAddEnumerable(ServiceDescriptor) mendaftarkan layanan hanya jika belum ada implementasi dengan jenis yang sama. Beberapa layanan diselesaikan melalui IEnumerable<{SERVICE}>. Saat mendaftarkan layanan, tambahkan instance jika belum ada satu pun yang sejenis yang sudah ditambahkan. Penulis pustaka menggunakan TryAddEnumerable untuk menghindari mendaftarkan beberapa salinan implementasi dalam kontainer.
Dalam contoh berikut, panggilan pertama untuk TryAddEnumerable mendaftar MessageWriter sebagai implementasi untuk IMessageWriter1. Panggilan kedua mendaftar MessageWriter untuk IMessageWriter2. Panggilan ketiga tidak berpengaruh karena IMessageWriter1 sudah memiliki implementasi terdaftar dari MessageWriter:
public interface IMessageWriter1 { }
public interface IMessageWriter2 { }
public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
Pendaftaran layanan tidak bergantung pada urutan kecuali saat mendaftarkan beberapa implementasi dengan jenis yang sama.
IServiceCollection adalah kumpulan ServiceDescriptor objek. Contoh berikut menunjukkan cara mendaftarkan layanan dengan membuat dan menambahkan ServiceDescriptor:
string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
typeof(IMessageWriter),
_ => new DefaultMessageWriter(secretKey),
ServiceLifetime.Transient);
services.Add(descriptor);
Metode bawaan Add{LIFETIME} menggunakan pendekatan yang sama. Misalnya, lihat kode sumber AddScoped.
Perilaku injeksi konstruktor
Layanan dapat diselesaikan menggunakan:
- IServiceProvider
-
ActivatorUtilities:
- Membuat objek yang tidak terdaftar dalam kontainer.
- Digunakan dengan beberapa fitur kerangka kerja.
Konstruktor dapat menerima argumen yang tidak disediakan oleh injeksi dependensi, tetapi argumen harus menetapkan nilai default.
Saat IServiceProvider atau ActivatorUtilities menyelesaikan layanan, injeksi konstruktor memerlukan konstruktor publik .
Ketika ActivatorUtilities menyelesaikan layanan, injeksi konstruktor mengharuskan hanya satu konstruktor yang berlaku. Kelebihan beban konstruktor didukung, tetapi hanya satu kelebihan beban yang dapat ada yang argumennya semuanya dapat dipenuhi oleh injeksi dependensi.
Validasi cakupan
Saat aplikasi berjalan di Development lingkungan dan memanggil CreateApplicationBuilder untuk membangun host, penyedia layanan default melakukan pemeriksaan untuk memverifikasi bahwa:
- Layanan terlingkup tidak diselesaikan dari penyedia layanan akar.
- Layanan terlingkup tidak disuntikkan ke dalam singleton.
Penyedia layanan akar dibuat ketika BuildServiceProvider dipanggil. Masa pakai penyedia layanan akar sesuai dengan masa pakai aplikasi saat penyedia mulai dengan aplikasi dan dibuang saat aplikasi dimatikan.
Layanan tercakup dibuang oleh kontainer yang membuatnya. Jika layanan tercakup dibuat dalam kontainer akar, masa pakai layanan secara efektif dipromosikan ke singleton karena hanya dibuang oleh kontainer akar saat aplikasi dimatikan. Memvalidasi cakupan layanan menangkap situasi ini ketika BuildServiceProvider dipanggil.
Skenario cakupan
IServiceScopeFactory selalu terdaftar sebagai singleton, tetapi IServiceProvider dapat bervariasi berdasarkan masa pakai kelas yang berisi. Misalnya, jika Anda mengambil layanan dari cakupan, dan jika salah satu layanan tersebut membutuhkan IServiceProvider, itu merupakan instance dalam ruang lingkup tertentu.
Untuk mencapai layanan cakupan dalam implementasi IHostedService, seperti BackgroundService, jangan menyuntikkan dependensi layanan melalui injeksi konstruktor. Sebagai gantinya, masukkan IServiceScopeFactory, buat cakupan, lalu atasi dependensi dari cakupan untuk menggunakan masa pakai layanan yang sesuai.
namespace WorkerScope.Example;
public sealed class Worker(
ILogger<Worker> logger,
IServiceScopeFactory serviceScopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
try
{
logger.LogInformation(
"Starting scoped work, provider hash: {hash}.",
scope.ServiceProvider.GetHashCode());
var store = scope.ServiceProvider.GetRequiredService<IObjectStore>();
var next = await store.GetNextAsync();
logger.LogInformation("{next}", next);
var processor = scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
await processor.ProcessAsync(next);
logger.LogInformation("Processing {name}.", next.Name);
var relay = scope.ServiceProvider.GetRequiredService<IObjectRelay>();
await relay.RelayAsync(next);
logger.LogInformation("Processed results have been relayed.");
var marked = await store.MarkAsync(next);
logger.LogInformation("Marked as processed: {next}", marked);
}
finally
{
logger.LogInformation(
"Finished scoped work, provider hash: {hash}.{nl}",
scope.ServiceProvider.GetHashCode(), Environment.NewLine);
}
}
}
}
}
Dalam kode sebelumnya, saat aplikasi sedang berjalan, layanan latar belakang:
- Tergantung pada IServiceScopeFactory.
- Membuat IServiceScope untuk menyelesaikan layanan lain.
- Menyelesaikan layanan terlingkup untuk dikonsumsi.
- Bekerja pada pemrosesan objek dan kemudian menyampaikannya, dan akhirnya menandainya sebagai diproses.
Dari kode sumber sampel, Anda dapat melihat bagaimana implementasi IHostedService dapat memperoleh manfaat dari masa pakai layanan terlingkup.
Layanan utama
Dimulai dengan .NET 8, ada dukungan untuk pendaftaran layanan dan pencarian berdasarkan kunci, yang berarti dimungkinkan untuk mendaftarkan beberapa layanan dengan kunci yang berbeda, dan menggunakan kunci ini untuk pencarian.
Misalnya, pertimbangkan kasus di mana Anda memiliki implementasi antarmuka IMessageWriteryang berbeda : MemoryMessageWriter dan QueueMessageWriter.
Anda dapat mendaftarkan layanan ini menggunakan kelebihan beban metode pendaftaran layanan (terlihat sebelumnya) yang mendukung kunci sebagai parameter:
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
key tidak terbatas pada string.
key Bisa menjadi apa pun yang object Anda inginkan, selama jenisnya mengimplementasikan Equalsdengan benar .
Di konstruktor kelas yang menggunakan IMessageWriter, Anda menambahkan FromKeyedServicesAttribute untuk menentukan kunci layanan untuk mengatasi:
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}
Lihat juga
- Memahami dasar-dasar injeksi dependensi di .NET
- Gunakan injeksi dependensi di .NET
- Panduan injeksi dependensi
- Injeksi dependensi di ASP.NET Core
- Pola Konferensi NDC untuk pengembangan aplikasi DI
- Prinsip dependensi eksplisit
- Inversi kontainer kontrol dan pola injeksi dependensi (Martin Fowler)
- Bug DI harus dibuat di repositori github.com/dotnet/extensions