Jak rozwiązać wystąpienie Inside ConfigureServices w ASP.NET Core

111

Czy można rozwiązać wystąpienie IOptions<AppSettings>z ConfigureServicesmetody w programie Startup? Zwykle możesz użyć IServiceProviderdo zainicjowania instancji, ale nie masz go na tym etapie, gdy rejestrujesz usługi.

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

    // How can I resolve IOptions<AppSettings> here?
}
Muhammad Rehan Saeed
źródło

Odpowiedzi:

160

Usługodawcę można zbudować za pomocą BuildServiceProvider()metody na 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>>();
}

Potrzebujesz Microsoft.Extensions.DependencyInjectiondo tego paczki.


W przypadku, gdy potrzebujesz tylko powiązać niektóre opcje ConfigureServices, możesz również użyć Bindmetody:

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

Ta funkcja jest dostępna w Microsoft.Extensions.Configuration.Binderpakiecie.

Henk Mollema
źródło
A jeśli musisz rozwiązać tę usługę w innej części aplikacji? Jestem pewien, że to nie wszystko jest zrobione w ConfigureServices (), prawda?
Ray
2
@Ray następnie możesz użyć domyślnych mechanizmów iniekcji zależności, takich jak iniekcja konstruktora. To pytanie dotyczy w szczególności rozwiązywania usług w ramach ConfigureServicesmetody.
Henk Mollema
@pcdev Otrzymujesz NULL, gdy to zrobisz, a następnie spróbujesz rozwiązać instancję. Musisz najpierw dodać usługę.
IngoB,
@IngoB tak, przepraszam, ten komentarz był niepoprawny i powinien zostać usunięty - nie rozumiałem dobrze, co się działo, kiedy pisałem ten komentarz. Proszę zapoznać się z odpowiedzią, z którą łączyłem się wcześniej, a która od tego czasu została zaktualizowana - przeprowadziłem dalsze dochodzenie i teraz lepiej ją rozumiem.
pcdev
16
Chociaż może to być przydatne w przypadkach, gdy metoda dodawania usługi nie ma przeciążenia fabryki implementacji (np. Tutaj ), użycie BuildServiceProviderpowoduje ostrzeżenie, jeśli jest używane w kodzie aplikacji, na przykład ConfigureServicespowoduje powstanie dodatkowej kopii pojedynczych usług. Utworzony. Odpowiedź Ehsana Mirsaeedi jest tutaj najbardziej idealnym rozwiązaniem w takich przypadkach.
Neo
76

Najlepszym sposobem tworzenia wystąpień klas zależnych od innych usług jest użycie przeciążenia Add XXX, które udostępnia IServiceProvider . W ten sposób nie musisz tworzyć instancji pośredniego dostawcy usług.

Poniższe przykłady pokazują, jak można użyć tego przeciążenia w metodach 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
źródło
19
Skorzystaj z tego rozwiązania zamiast akceptowanej odpowiedzi dla .Net Core 3 lub nowszego!
Joshit
11
@Joshit Nie jestem pewien, czy jest to realny zamiennik zaakceptowanej odpowiedzi we wszystkich scenariuszach. IServiceProvider jest dostępny m.in. dla AddSingleton, AddScoped, AddTransient. Ale istnieje wiele innych metod Add, które nie zapewniają tego przeciążenia, np. AddCors, AddAuthentication, AddAuthorization.
Jpsy
1
@Jpsy Mylisz rzeczy niezwiązane. AddCors, AddAuthentication i tak dalej są pomocnikami, które wywołują pod emodami rejestracji, aby połączyć różne podstawowe oprogramowanie pośredniczące. AddTransient, AddSingleton, AddScoped to trzy rejestracje (z trzema często używanymi okresami istnienia)
Fab
Nie obejmuje to wszystkich przypadków. Zapoznaj się z moją odpowiedzią, aby znaleźć rozwiązanie, które to robi.
Ian Kemp
11

Najprostszym i najbardziej poprawnym sposobem osiągnięcia tego we wszystkich wersjach ASP.NET Core jest zaimplementowanie IConfigureOptions<TOptions>interfejsu. Chociaż było to już od .NET Core 1.0, wydaje się, że niewiele osób wie o tym, jak to wszystko działa .

Na przykład chcesz dodać moduł weryfikacji modelu niestandardowego, który jest zależny od jednej z innych usług aplikacji. Początkowo wydaje się to niemożliwe - nie ma sposobu na rozwiązanie, IMyServiceDependencyponieważ nie masz dostępu do IServiceProvider:

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

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

Ale „magia” IConfigureOptions<TOptions>sprawia, że ​​jest to takie proste:

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

Zasadniczo każda konfiguracja, którą wykonałeś w Add***(***Options)delegatach, ConfigureServicesjest teraz przenoszona do metody Twojej IConfigureOptions<TOptions>klasy Configure. Następnie rejestrujesz opcje w ten sam sposób, w jaki rejestrujesz każdą inną usługę i gotowe!

Aby uzyskać więcej szczegółów, a także informacje o tym, jak to działa za kulisami, odsyłam do zawsze doskonałego Andrew Lock .

Ian Kemp
źródło
1

Szukasz czegoś takiego jak śledzenie? Możesz spojrzeć na moje komentarze w kodzie:

// 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
źródło
-1

Chcesz pomóc innym, którzy wyglądają tak samo, ale także podczas korzystania z Autofac.

Jeśli chcesz uzyskać ILifetimeScope (tj. Kontener o aktualnym zakresie), musisz wywołać app.ApplicationServices.GetAutofacRoot()metodę, Configure(IApplicationBuilder app)która zwróci instancję ILifetimeScope, której możesz użyć do rozwiązania usług

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

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }
po prostu dobre
źródło
2
Ta odpowiedź jest zbyt specyficzna dla AutoFac, która nie wchodzi w zakres tego pytania.
Pure.Krome
Przyszedłem tutaj, wpisując w Google to pytanie z prefiksem autofac i niestety nie znalazłem żadnego konkretnego tematu. Spodziewam się więc, że inni, którzy również dojdą do tego pytania borykając się z tym problemem, znajdą odpowiedź.
po prostu dobry