Come risolvere l'istanza all'interno di ConfigureServices in ASP.NET Core


101

È possibile risolvere un'istanza di IOptions<AppSettings>dal ConfigureServicesmetodo in Startup? Normalmente è possibile utilizzare IServiceProviderper inizializzare le istanze, ma in questa fase non è disponibile quando si registrano i servizi.

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

    // How can I resolve IOptions<AppSettings> here?
}

Risposte:


152

Puoi creare un fornitore di servizi utilizzando il BuildServiceProvider()metodo su 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>>();
}

Hai bisogno del Microsoft.Extensions.DependencyInjectionpacchetto per questo.


Nel caso in cui devi solo associare alcune opzioni ConfigureServices, puoi anche utilizzare il Bindmetodo:

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

Questa funzionalità è disponibile tramite il Microsoft.Extensions.Configuration.Binderpacchetto.


E se fosse necessario risolvere questo servizio in un'altra parte dell'applicazione? Sono sicuro che non sia tutto fatto in ConfigureServices (), giusto?
Ray

1
@ Ray quindi puoi utilizzare i meccanismi di inserimento delle dipendenze predefiniti come l'iniezione del costruttore. Questa domanda riguarda specificamente la risoluzione dei servizi all'interno del ConfigureServicesmetodo.
Henk Mollema

@pcdev Ottieni NULL quando lo fai e poi provi a risolvere l'istanza. Devi prima aggiungere il servizio.
IngoB

@ IngoB sì, mi dispiace, quel commento non era corretto e dovrebbe essere cancellato - Non ho capito bene cosa stesse succedendo quando ho scritto quel commento. Si prega di vedere la risposta a cui ho collegato in precedenza e che ho aggiornato da allora: ho svolto ulteriori indagini e ora la capisco meglio.
pcdev

14
Anche se questo può essere utile nei casi in cui il metodo per aggiungere un servizio non ha un sovraccarico di fabbrica di implementazione (ad esempio, qui ), l'utilizzo BuildServiceProviderprovoca un avviso se utilizzato nel codice dell'applicazione, in ConfigureServicesquanto risulta in una copia aggiuntiva dei servizi singleton creato. La risposta di Ehsan Mirsaeedi qui è la soluzione più ideale per casi come questo.
Neo

65

Il modo migliore per creare un'istanza delle classi che dipendono da altri servizi consiste nell'usare l' overload di Add XXX che fornisce IServiceProvider . In questo modo non è necessario creare un'istanza di un fornitore di servizi intermedio.

Gli esempi seguenti mostrano come utilizzare questo overload nei metodi 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;
});

16
Usa questa soluzione invece della risposta accettata per .Net Core 3 o superiore!
Joshit

7
@ Joshit Non sono così sicuro che questo sia un valido sostituto per la risposta accettata in tutti gli scenari. IServiceProvider è disponibile per esempio AddSingleton, AddScoped, AddTransient. Ma ci sono molti altri metodi Add che non forniscono questo overload, ad esempio AddCors, AddAuthentication, AddAuthorization.
Jpsy

1
@ Jpsy Mescoli cose non correlate. AddCors, AddAuthentication e così via sono helper che chiamano sotto gli emthod di registrazione per collegare i vari middleware sottostanti. AddTransient, AddSingleton, AddScoped sono le tre registrazioni (con le tre durate comunemente usate)
Fab

Questo non copre tutti i casi. Si prega di fare riferimento alla mia risposta per una soluzione che fa.
Ian Kemp

7

Il modo più semplice e corretto per ottenere ciò, in tutte le versioni di ASP.NET Core , è implementare l' IConfigureOptions<TOptions>interfaccia. Sebbene questo sia in circolazione da .NET Core 1.0, sembra che poche persone sappiano come fa le cose Just Work ™.

Ad esempio, si desidera aggiungere un validatore di modelli personalizzato che abbia una dipendenza da uno degli altri servizi dell'applicazione. Inizialmente sembra impossibile: non c'è modo di risolvere IMyServiceDependencyperché non hai accesso a un IServiceProvider:

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

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

Ma la "magia" di IConfigureOptions<TOptions>rende tutto così facile:

public class MyMvcOptions : 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)
{
    // or scoped, or transient
    services.AddSingleton<IConfigureOptions<MvcOptions>, MyMvcOptions>();
    services.AddControllers();
}

In sostanza, qualsiasi configurazione che avresti fatto nei Add***(***Options)delegati in ConfigureServicesviene ora spostata nel metodo IConfigureOptions<TOptions>della tua classe Configure. Quindi registri le opzioni nello stesso modo in cui registri qualsiasi altro servizio e via!

Per maggiori dettagli, oltre che per informazioni su come funziona dietro le quinte, vi rimando al sempre eccellente Andrew Locke .


1

Cerchi qualcosa come seguire? Puoi dare un'occhiata ai miei commenti nel codice:

// 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

-1

Vuoi aiutare gli altri che hanno lo stesso aspetto ma anche quando usano Autofac.

Se vuoi ottenere ILifetimeScope (cioè il contenitore dell'ambito corrente) devi chiamare il app.ApplicationServices.GetAutofacRoot()metodo in Configure(IApplicationBuilder app)questo restituirà l'istanza di ILifetimeScope che puoi usare per risolvere i servizi

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

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }

1
Questa risposta è troppo specifica per AutoFac, che non rientra nello scopo di questa domanda.
Pure.Krome

Sono venuto qui cercando su Google questa domanda con il prefisso autofac e sfortunatamente non ho trovato alcun argomento specifico. Quindi mi aspetto che anche altri che verranno a questa domanda alle prese con questo problema possano trovare una risposta.
semplicemente buono il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.