Auflösen der Instanz in ConfigureServices in ASP.NET Core

99

Ist es möglich, eine Instanz von IOptions<AppSettings>aus der ConfigureServicesMethode in Startup aufzulösen ? Normalerweise können Sie IServiceProviderInstanzen initialisieren, aber Sie haben sie zu diesem Zeitpunkt noch nicht, wenn Sie Dienste registrieren.

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

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

Antworten:

150

Sie können einen Dienstanbieter mithilfe der folgenden BuildServiceProvider()Methode erstellen 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>>();
}

Sie benötigen das Microsoft.Extensions.DependencyInjectionPaket dafür.


In dem Fall, in dem Sie nur einige Optionen einbinden müssen ConfigureServices, können Sie auch die folgende BindMethode verwenden:

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

Diese Funktionalität ist über das Microsoft.Extensions.Configuration.BinderPaket verfügbar .

Henk Mollema
quelle
Was ist, wenn Sie diesen Dienst in einem anderen Teil der Anwendung auflösen müssen? Ich bin sicher, dass in ConfigureServices () nicht alles erledigt ist, oder?
Ray
1
@Ray, dann können Sie die Standard-Abhängigkeitsinjektionsmechanismen wie die Konstruktorinjektion verwenden. Bei dieser Frage geht es speziell um das Auflösen von Diensten innerhalb der ConfigureServicesMethode.
Henk Mollema
@pcdev Wenn Sie dies tun, erhalten Sie NULL und versuchen dann, die Instanz aufzulösen. Sie müssen zuerst den Dienst hinzufügen.
IngoB
@IngoB Ja, tut mir leid, dieser Kommentar war falsch und sollte gelöscht werden. Ich habe nicht richtig verstanden, was los war, als ich diesen Kommentar schrieb. Bitte lesen Sie die Antwort, auf die ich zuvor verlinkt habe und die ich seitdem aktualisiert habe. Ich habe weitere Nachforschungen angestellt und verstehe sie jetzt besser.
PCDEV
13
Während dies in Fällen nützlich sein kann, in denen die Methode zum Hinzufügen eines Dienstes keine Überlastung der Implementierungsfactory aufweist (z. B. hier ), BuildServiceProviderverursacht die Verwendung eine Warnung, wenn sie im Anwendungscode verwendet wird, z. B. ConfigureServicesweil dadurch eine zusätzliche Kopie der Singleton-Dienste erstellt wird erstellt. Die Antwort von Ehsan Mirsaeedi hier ist die idealste Lösung für solche Fälle.
Neo
65

Der beste Weg, um Klassen zu instanziieren, die von anderen Diensten abhängig sind, ist die Verwendung der Überladung Add XXX , mit der Sie den IServiceProvider erhalten . Auf diese Weise müssen Sie keinen Zwischendienstanbieter instanziieren.

Die folgenden Beispiele zeigen, wie Sie diese Überladung in AddSingleton / AddTransient- Methoden verwenden können.

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
quelle
16
Verwenden Sie diese Lösung anstelle der akzeptierten Antwort für .Net Core 3 oder höher!
Joshit
7
@Joshit Ich bin mir nicht sicher, ob dies in allen Szenarien ein praktikabler Ersatz für die akzeptierte Antwort ist. IServiceProvider ist verfügbar für zB AddSingleton, AddScoped, AddTransient. Es gibt jedoch viele andere Add-Methoden, die diese Überlastung nicht bereitstellen, z. B. AddCors, AddAuthentication, AddAuthorization.
Jpsy
@Jpsy Du vermischst Dinge, die nichts miteinander zu tun haben. AddCors, AddAuthentication usw. sind Helfer, die unter den Registrierungsmethoden aufrufen, um die verschiedenen zugrunde liegenden Middleware zu verkabeln. AddTransient, AddSingleton, AddScoped sind die drei Registrierungen (mit den drei häufig verwendeten Lebensdauern)
Fab
Dies deckt nicht alle Fälle ab. Bitte beziehen Sie sich auf meine Antwort für eine Lösung, die dies tut.
Ian Kemp
6

Der einfachste und korrekteste Weg, dies in allen Versionen von ASP.NET Core zu erreichen , ist die Implementierung der IConfigureOptions<TOptions>Schnittstelle. Obwohl dies seit .NET Core 1.0 der Fall ist, scheinen nur wenige Menschen zu wissen, wie es die Dinge zu Just Work ™ macht.

Als Beispiel möchten Sie einen benutzerdefinierten Modellvalidator hinzufügen, der von einem der anderen Dienste Ihrer Anwendung abhängig ist. Zunächst scheint es unmöglich - es gibt keine Möglichkeit zur Lösung, IMyServiceDependencyda Sie keinen Zugriff auf Folgendes haben IServiceProvider:

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

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

Aber die "Magie" von IConfigureOptions<TOptions>macht es so einfach:

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

Im Wesentlichen wird jedes Setup, das Sie in den Add***(***Options)Delegaten in vorgenommen haben, ConfigureServicesjetzt in IConfigureOptions<TOptions>die ConfigureMethode Ihrer Klasse verschoben . Dann registrieren Sie die Optionen auf die gleiche Weise, wie Sie jeden anderen Dienst registrieren würden, und los geht's!

Für weitere Details sowie Informationen darüber, wie dies hinter den Kulissen funktioniert, verweise ich Sie auf den immer ausgezeichneten Andrew Locke .

Ian Kemp
quelle
1

Suchen Sie etwas wie folgendes? Sie können sich meine Kommentare im Code ansehen:

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

Möchten Sie anderen helfen, die gleich aussehen, aber auch Autofac verwenden?

Wenn Sie ILifetimeScope (dh einen Container mit aktuellem Gültigkeitsbereich) abrufen möchten, müssen Sie die app.ApplicationServices.GetAutofacRoot()Methode aufrufen , Configure(IApplicationBuilder app)um die ILifetimeScope-Instanz zurückzugeben, mit der Sie Dienste auflösen können

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

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }
einfach gut
quelle
1
Diese Antwort ist für AutoFac zu spezifisch, was nicht im Rahmen dieser Frage liegt.
Pure.Krome
Ich bin hierher gekommen, indem ich diese Frage mit dem Autofac-Präfix gegoogelt habe und leider kein bestimmtes Thema gefunden habe. Ich gehe davon aus, dass andere, die ebenfalls mit diesem Problem zu kämpfen haben, eine Antwort finden können.
einfach gut