Memiliki konstruktor layanan berikut
public class Service : IService
{
public Service(IOtherService service1, IAnotherOne service2, string arg)
{
}
}
Apa saja pilihan untuk meneruskan parameter menggunakan mekanisme .NET Core IOC
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( _serviceCollection.BuildServiceProvider().GetService<IOtherService>(), _serviceCollection.BuildServiceProvider().GetService<IAnotherOne >(), "" ));
Apakah ada cara lain?
Jawaban:
Parameter ekspresi ( x dalam kasus ini), dari delegasi pabrik adalah a
IServiceProvider
.Gunakan itu untuk menyelesaikan dependensi,
_serviceCollection.AddSingleton<IService>(x => new Service(x.GetRequiredService<IOtherService>(), x.GetRequiredService<IAnotherOne>(), ""));
Delegasi pabrik adalah permintaan yang tertunda. Kapan pun jenisnya akan diselesaikan, ia akan meneruskan penyedia yang telah diselesaikan sebagai parameter delegasi.
sumber
.WithParameter("argument", "");
Microsoft.Extensions.DependencyInjection.Abstractions
paket (jadi tidak ada dependensi khusus container)Perlu dicatat bahwa cara yang disarankan adalah dengan menggunakan Pola Opsi . Tetapi ada kasus penggunaan yang tidak praktis (ketika parameter hanya diketahui saat runtime, bukan pada waktu startup / kompilasi) atau Anda perlu mengganti dependensi secara dinamis.
Ini sangat berguna saat Anda perlu mengganti satu dependensi (baik itu string, integer, atau jenis dependensi lain) atau saat menggunakan library pihak ketiga yang hanya menerima parameter string / integer dan Anda memerlukan parameter waktu proses.
Anda dapat mencoba CreateInstance (IServiceProvider, Object []) sebagai jalan pintas
(tidak yakin ini berfungsi dengan parameter string / tipe nilai / primitif (int, float, string), belum diuji)(Baru saja mencobanya dan mengonfirmasi kerjanya, bahkan dengan beberapa parameter string) daripada menyelesaikan setiap dependensi dengan tangan:_serviceCollection.AddSingleton<IService>(x => ActivatorUtilities.CreateInstance<Service>(x, ""); );
Parameter (parameter terakhir dari
CreateInstance<T>
/CreateInstance
) menentukan parameter yang harus diganti (tidak diselesaikan dari penyedia). Mereka diterapkan dari kiri ke kanan saat muncul (yaitu string pertama akan diganti dengan parameter tipe string pertama yang akan dipakai).ActivatorUtilities.CreateInstance<Service>
digunakan di banyak tempat untuk menyelesaikan layanan dan menggantikan salah satu registrasi default untuk aktivasi tunggal ini.Misalnya jika Anda memiliki kelas bernama
MyService
, dan memilikiIOtherService
,ILogger<MyService>
sebagai dependensi dan Anda ingin menyelesaikan layanan tetapi mengganti layanan defaultIOtherService
(katakanlahOtherServiceA
) denganOtherServiceB
, Anda dapat melakukan sesuatu seperti:myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())
Kemudian parameter pertama
IOtherService
akanOtherServiceB
diinjeksikan, bukanOtherServiceA
parameter yang tersisa akan datang dari container.Ini berguna ketika Anda memiliki banyak dependensi dan hanya ingin memperlakukan satu dependensi secara khusus (yaitu mengganti penyedia khusus database dengan nilai yang dikonfigurasi selama permintaan atau untuk pengguna tertentu, sesuatu yang hanya Anda ketahui pada waktu proses dan selama permintaan dan bukan saat aplikasi dibangun / dimulai).
Anda juga dapat menggunakan Metode ActivatorUtilities.CreateFactory (Type, Type []) untuk membuat metode pabrik, karena metode ini menawarkan Referensi dan Tolok Ukur GitHub dengan kinerja yang lebih baik .
Nanti berguna ketika tipe diselesaikan sangat sering (seperti di SignalR dan skenario permintaan tinggi lainnya). Pada dasarnya Anda akan membuat
ObjectFactory
melaluivar myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });
kemudian cache (sebagai variabel dll) dan panggil jika diperlukan
## Pembaruan: Coba sendiri untuk mengonfirmasi bahwa ini juga berfungsi dengan string dan integer, dan memang berhasil. Berikut contoh konkret yang saya uji dengan:
class Program { static void Main(string[] args) { var services = new ServiceCollection(); services.AddTransient<HelloWorldService>(); services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow")); var provider = services.BuildServiceProvider(); var demoService = provider.GetRequiredService<DemoService>(); Console.WriteLine($"Output: {demoService.HelloWorld()}"); Console.ReadKey(); } } public class DemoService { private readonly HelloWorldService helloWorldService; private readonly string firstname; private readonly string lastname; public DemoService(HelloWorldService helloWorldService, string firstname, string lastname) { this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService)); this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname)); this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname)); } public string HelloWorld() { return this.helloWorldService.Hello(firstName, lastName); } } public class HelloWorldService { public string Hello(string name) => $"Hello {name}"; public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}"; } // Just a helper method to shorten code registration code static class ServiceProviderExtensions { public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class => ActivatorUtilities.CreateInstance<T>(provider, parameters); }
Cetakan
sumber
.AddControllersAsServices
digunakan, yang menggantikanControllerActivatorProvider
denganServiceBasedControllerActivator
Jika Anda merasa tidak nyaman dengan layanan baru, Anda dapat menggunakan
Parameter Object
polanya.Jadi ekstrak parameter string ke dalam tipenya sendiri
public class ServiceArgs { public string Arg1 {get; set;} }
Dan konstruktornya sekarang akan terlihat seperti
public Service(IOtherService service1, IAnotherOne service2, ServiceArgs args) { }
Dan penyiapannya
_serviceCollection.AddSingleton<ServiceArgs>(_ => new ServiceArgs { Arg1 = ""; }); _serviceCollection.AddSingleton<IOtherService , OtherService>(); _serviceCollection.AddSingleton<IAnotherOne , AnotherOne>(); _serviceCollection.AddSingleton<IService, Service>();
Manfaat pertama adalah jika Anda perlu mengubah konstruktor Layanan dan menambahkan layanan baru padanya, Anda tidak perlu mengubah
new Service(...
panggilannya. Manfaat lainnya adalah pengaturannya sedikit lebih bersih.Untuk konstruktor dengan satu atau dua parameter, ini mungkin terlalu banyak.
sumber
Anda juga dapat memasukkan dependensi dengan proses ini
_serviceCollection.AddSingleton<IOtherService , OtherService>(); _serviceCollection.AddSingleton<IAnotherOne , AnotherOne>(); _serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));
sumber