Comment résoudre une instance dans ConfigureServices dans ASP.NET Core

103

Est-il possible de résoudre une instance de à IOptions<AppSettings>partir de la ConfigureServicesméthode dans Startup? Normalement, vous pouvez utiliser IServiceProviderpour initialiser des instances, mais vous ne l'avez pas à ce stade lorsque vous enregistrez des services.

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

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

Réponses:

154

Vous pouvez créer un fournisseur de services en utilisant la BuildServiceProvider()méthode sur 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>>();
}

Vous avez besoin du Microsoft.Extensions.DependencyInjectionpackage pour cela.


Dans le cas où vous avez juste besoin de lier certaines options ConfigureServices, vous pouvez également utiliser la Bindméthode:

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

Cette fonctionnalité est disponible via le Microsoft.Extensions.Configuration.Binderpackage.

Henk Mollema
la source
Que faire si vous devez résoudre ce service dans une autre partie de l'application? Je suis sûr que tout n'est pas fait dans ConfigureServices (), n'est-ce pas?
Ray
1
@Ray, vous pouvez alors utiliser les mécanismes d'injection de dépendances par défaut tels que l'injection de constructeur. Cette question concerne spécifiquement la résolution des services à l'intérieur de la ConfigureServicesméthode.
Henk Mollema
@pcdev Vous obtenez NULL lorsque vous le faites, puis essayez de résoudre l'instance. Vous devez d'abord ajouter le service.
IngoB
@IngoB ouais, désolé, ce commentaire était incorrect et devrait être supprimé - je n'ai pas bien compris ce qui se passait quand j'ai écrit ce commentaire. S'il vous plaît voir la réponse que j'ai liée précédemment et que j'ai depuis mise à jour - j'ai fait une enquête plus approfondie et je la comprends maintenant mieux.
pcdev
15
Bien que cela puisse être utile dans les cas où la méthode pour ajouter un service n'a pas de surcharge de fabrique d'implémentation (par exemple, ici ), l'utilisation BuildServiceProviderprovoque un avertissement si elle est utilisée dans le code d'application, par exemple, ConfigureServicescar cela entraîne une copie supplémentaire des services singleton. établi. La réponse d'Ehsan Mirsaeedi ici est la solution la plus idéale pour des cas comme celui-ci.
Neo
67

La meilleure façon d'instancier des classes qui dépendent d'autres services consiste à utiliser la surcharge Add XXX qui vous fournit IServiceProvider . De cette façon, vous n'avez pas besoin d'instancier un fournisseur de services intermédiaire.

Les exemples suivants montrent comment vous pouvez utiliser cette surcharge dans les méthodes 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
la source
16
Utilisez cette solution plutôt que la réponse acceptée pour .Net Core 3 ou supérieur!
Joshit
7
@Joshit Je ne suis pas sûr que ce soit un remplacement viable pour la réponse acceptée dans tous les scénarios. IServiceProvider est disponible pour par exemple AddSingleton, AddScoped, AddTransient. Mais il existe de nombreuses autres méthodes Add qui ne fournissent pas cette surcharge, à savoir AddCors, AddAuthentication, AddAuthorization.
Jpsy
1
@Jpsy Vous mélangez des choses sans rapport. AddCors, AddAuthentication et ainsi de suite sont des assistants qui appellent sous les emthodes d'enregistrement pour câbler les différents middleware sous-jacents. AddTransient, AddSingleton, AddScoped sont les trois enregistrements (avec les trois durées de vie couramment utilisées)
Fab
Cela ne couvre pas tous les cas. Veuillez vous référer à ma réponse pour une solution qui fonctionne.
Ian Kemp
8

Le moyen le plus simple et le plus correct d'y parvenir, dans toutes les versions d'ASP.NET Core , consiste à implémenter l' IConfigureOptions<TOptions>interface. Bien que cela existe depuis .NET Core 1.0, il semble que peu de gens sachent comment les choses fonctionnent Just Work ™ .

Par exemple, vous souhaitez ajouter un validateur de modèle personnalisé qui a une dépendance sur l'un des autres services de votre application. Au départ, cela semble impossible - il n'y a aucun moyen de résoudre IMyServiceDependencycar vous n'avez pas accès à un IServiceProvider:

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

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

Mais la "magie" du IConfigureOptions<TOptions>rend les choses si faciles:

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>();
}

Essentiellement, toute configuration que vous auriez effectuée dans les Add***(***Options)délégués ConfigureServicesest maintenant déplacée vers IConfigureOptions<TOptions>la Configureméthode de votre classe . Ensuite, vous enregistrez les options de la même manière que vous enregistrez n'importe quel autre service, et c'est parti!

Pour plus de détails, ainsi que des informations sur la façon dont cela fonctionne dans les coulisses, je vous renvoie au toujours excellent Andrew Lock .

Ian Kemp
la source
1

Cherchez-vous quelque chose comme suivre? Vous pouvez jeter un oeil à mes commentaires dans le code:

// 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
la source
-1

Vous voulez aider les autres qui ressemblent à la même chose, mais qui utilisent également Autofac.

Si vous voulez obtenir ILifetimeScope (c'est-à-dire le conteneur de la portée actuelle), vous devez appeler la app.ApplicationServices.GetAutofacRoot()méthode Configure(IApplicationBuilder app)qui renverra l'instance ILifetimeScope que vous pouvez utiliser pour résoudre les services

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

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }
tout simplement bon
la source
1
Cette réponse est trop spécifique à AutoFac, qui n'entre pas dans le cadre de cette question.
Pure.Krome le
Je suis venu ici en recherchant sur Google cette question avec le préfixe autofac et je n'ai malheureusement trouvé aucun sujet spécifique. J'espère donc que d'autres qui viendront également à cette question aux prises avec ce problème pourront trouver une réponse.
tout simplement bon le