Bagaimana mengatasi Instance Inside ConfigureServices di ASP.NET Core

105

Apakah mungkin untuk menyelesaikan sebuah contoh dari IOptions<AppSettings>dari ConfigureServicesmetode dalam Startup? Biasanya Anda dapat menggunakan IServiceProvideruntuk menginisialisasi instance, tetapi Anda tidak memilikinya pada tahap ini saat Anda mendaftarkan layanan.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<AppSettings>(
        configuration.GetConfigurationSection(nameof(AppSettings)));

    // How can I resolve IOptions<AppSettings> here?
}
Muhammad Rehan Saeed
sumber

Jawaban:

155

Anda bisa membangun penyedia layanan menggunakan BuildServiceProvider()metode di IServiceCollection:

public void ConfigureService(IServiceCollection services)
{
    // Configure the services
    services.AddTransient<IFooService, FooServiceImpl>();
    services.Configure<AppSettings>(configuration.GetSection(nameof(AppSettings)));

    // Build an intermediate service provider
    var sp = services.BuildServiceProvider();

    // Resolve the services from the service provider
    var fooService = sp.GetService<IFooService>();
    var options = sp.GetService<IOptions<AppSettings>>();
}

Anda membutuhkan Microsoft.Extensions.DependencyInjectionpaket untuk ini.


Dalam kasus di mana Anda hanya perlu mengikat beberapa opsi ConfigureServices, Anda juga dapat menggunakan Bindmetode ini:

var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);

Fungsionalitas ini tersedia melalui Microsoft.Extensions.Configuration.Binderpaket.

Henk Mollema
sumber
Bagaimana jika Anda perlu menyelesaikan layanan ini di bagian lain aplikasi? Saya yakin tidak semuanya dilakukan di ConfigureServices (), kan?
Ray
1
@Ray maka Anda dapat menggunakan mekanisme injeksi dependensi default seperti injeksi konstruktor. Pertanyaan ini secara khusus tentang menyelesaikan layanan di dalam ConfigureServicesmetode.
Henk Mollema
@pcdev Anda mendapatkan NULL saat melakukannya dan kemudian mencoba untuk menyelesaikan instance. Anda harus menambahkan layanan terlebih dahulu.
IngoB
@IngoB ya, maaf, komentar itu salah dan harus dihapus - Saya tidak benar-benar mengerti apa yang terjadi ketika saya menulis komentar itu. Silakan lihat jawaban yang saya tautkan sebelumnya yang telah saya perbarui sejak itu - Saya melakukan penyelidikan lebih lanjut dan sekarang memahaminya dengan lebih baik.
pcdev
15
Meskipun ini mungkin berguna dalam kasus di mana metode untuk menambahkan layanan tidak memiliki implementasi yang berlebihan dari pabrik (misalnya, di sini ), penggunaan BuildServiceProvidermenyebabkan peringatan jika digunakan dalam kode aplikasi seperti ConfigureServicesitu menghasilkan salinan tambahan layanan tunggal yang dibuat. Jawaban Ehsan Mirsaeedi di sini adalah solusi paling ideal untuk kasus seperti ini.
Neo
68

Cara terbaik untuk membuat instance kelas yang bergantung pada layanan lain adalah dengan menggunakan kelebihan Add XXX yang memberi Anda IServiceProvider . Dengan cara ini Anda tidak perlu membuat contoh penyedia layanan perantara.

Contoh berikut menunjukkan bagaimana Anda dapat menggunakan kelebihan ini dalam metode AddSingleton / AddTransient .

services.AddSingleton(serviceProvider =>
{
    var options = serviceProvider.GetService<IOptions<AppSettings>>();
    var foo = new Foo(options);
    return foo ;
});


services.AddTransient(serviceProvider =>
{
    var options = serviceProvider.GetService<IOptions<AppSettings>>();
    var bar = new Bar(options);
    return bar;
});
Ehsan Mirsaeedi
sumber
16
Gunakan solusi ini daripada jawaban yang diterima untuk .Net Core 3 atau lebih tinggi!
Joshit
7
@Joshit Saya tidak begitu yakin bahwa ini adalah pengganti yang layak untuk jawaban yang diterima di semua skenario. IServiceProvider tersedia untuk AddSingleton, AddScoped, AddTransient. Tetapi ada banyak metode Tambah lain yang tidak memberikan kelebihan beban ini, yaitu AddCors, AddAuthentication, AddAuthorization.
Jpsy
1
@Jpsy Anda mencampur hal-hal yang tidak terkait. AddCors, AddAuthentication, dan sebagainya adalah pembantu yang memanggil di bawah emthod pendaftaran untuk menghubungkan berbagai middleware yang mendasarinya. AddTransient, AddSingleton, AddScoped adalah tiga registrasi (dengan tiga masa pakai yang umum digunakan)
Fab
Ini tidak mencakup semua kasus. Silakan lihat jawaban saya untuk solusi yang bisa.
Ian Kemp
8

Cara termudah dan paling benar untuk mencapai ini, di semua versi ASP.NET Core , adalah dengan mengimplementasikan IConfigureOptions<TOptions>antarmuka. Meskipun ini telah ada sejak .NET Core 1.0, tampaknya hanya sedikit orang yang tahu tentang cara membuat Just Work ™ .

Misalnya, Anda ingin menambahkan validator model kustom yang memiliki ketergantungan pada salah satu layanan aplikasi Anda yang lain. Awalnya tampaknya tidak mungkin - tidak ada cara untuk menyelesaikannya IMyServiceDependencykarena Anda tidak memiliki akses ke IServiceProvider:

public class MyModelValidatorProvider : IModelValidatorProvider
{
    public MyModelValidatorProvider(IMyServiceDependency dependency)
    {
        ...
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????));
    });
}

Tapi "keajaiban" IConfigureOptions<TOptions>membuatnya begitu mudah:

public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
    private IMyServiceDependency _dependency;

    public MyMvcOptions(IMyServiceDependency dependency)
        => _dependency = dependency;

    public void Configure(MvcOptions options)
        => options.ModelValidatorProviders.Add(new MyModelValidatorProvider(_dependency));
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    ...

    // or scoped, or transient, as necessary for your service
    services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();
}

Pada dasarnya, penyiapan apa pun yang seharusnya Anda lakukan di dalam Add***(***Options)delegasi ConfigureServicessekarang dipindahkan ke metode IConfigureOptions<TOptions>kelas Anda Configure. Kemudian Anda mendaftarkan opsi dengan cara yang sama seperti Anda mendaftarkan layanan lain, dan Anda pergi!

Untuk detail lebih lanjut, serta informasi tentang cara kerjanya di balik layar, saya merujuk Anda ke Andrew Lock yang selalu luar biasa .

Ian Kemp
sumber
1

Apakah Anda mencari sesuatu seperti berikut? Anda dapat melihat komentar saya di kode:

// this call would new-up `AppSettings` type
services.Configure<AppSettings>(appSettings =>
{
    // bind the newed-up type with the data from the configuration section
    ConfigurationBinder.Bind(appSettings, Configuration.GetConfigurationSection(nameof(AppSettings)));

    // modify these settings if you want to
});

// your updated app settings should be available through DI now
Kiran Challa
sumber
-1

Ingin membantu orang lain yang berpenampilan sama tetapi saat menggunakan Autofac juga.

Jika Anda ingin mendapatkan ILifetimeScope (yaitu kontainer dengan cakupan saat ini) Anda perlu memanggil app.ApplicationServices.GetAutofacRoot()metode Configure(IApplicationBuilder app)ini akan mengembalikan contoh ILifetimeScope yang dapat Anda gunakan untuk menyelesaikan layanan

public void Configure(IApplicationBuilder app)
    {
        //app middleware registrations 
        //...
        //

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }
cukup bagus
sumber
1
Jawaban ini terlalu spesifik untuk AutoFac, yang tidak termasuk dalam cakupan pertanyaan ini.
Pure.Krome
Saya datang ke sini dengan mencari pertanyaan ini di Google dengan awalan autofac dan sayangnya tidak menemukan topik khusus. Jadi saya berharap orang lain yang juga akan datang ke pertanyaan ini bergumul dengan masalah ini dapat menemukan jawabannya.
Cukup baik