ChatGPT解决这个技术问题 Extra ChatGPT

Resolving instances with ASP.NET Core DI from within ConfigureServices

How do I manually resolve a type using the ASP.NET Core MVC built-in dependency injection framework?

Setting up the container is easy enough:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

But how can I resolve ISomeService without performing injection? For example, I want to do this:

ISomeService service = services.Resolve<ISomeService>();

There are no such methods in IServiceCollection.

Do you want to resolve them in the ConfigureServices() method (with IServiceCollection) or just anywhere in the application?
@HenkMollema: Anywhere within Startup actually.
If you are looking to do this in .net core 3.1 stackoverflow.com/a/65245884

H
Henk Mollema

The IServiceCollection interface is used for building a dependency injection container. After it's fully built, it gets composed to an IServiceProvider instance which you can use to resolve services. You can inject an IServiceProvider into any class. The IApplicationBuilder and HttpContext classes can provide the service provider as well, via their ApplicationServices or RequestServices properties respectively.

IServiceProvider defines a GetService(Type type) method to resolve a service:

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

There are also several convenience extension methods available, such as serviceProvider.GetService<IFooService>() (add a using for Microsoft.Extensions.DependencyInjection).

Resolving services inside the startup class

Injecting dependencies

The runtime's hosting service provider can inject certain services into the constructor of the Startup class, such as IConfiguration, IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.

The ConfigureServices() method does not allow injecting services, it only accepts an IServiceCollection argument. This makes sense because ConfigureServices() is where you register the services required by your application. However you can use services injected in the startup's constructor here, for example:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

Any services registered in ConfigureServices() can then be injected into the Configure() method; you can add an arbitrary number of services after the IApplicationBuilder parameter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IFooService>();
}

public void Configure(IApplicationBuilder app, IFooService fooService)
{
    fooService.Bar();
}

Manually resolving dependencies

If you need to manually resolve services, you should preferably use the ApplicationServices provided by IApplicationBuilder in the Configure() method:

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

It is possible to pass and directly use an IServiceProvider in the constructor of your Startup class, but as above this will contain a limited subset of services, and thus has limited utility:

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}

If you must resolve services in the ConfigureServices() method, a different approach is required. You can build an intermediate IServiceProvider from the IServiceCollection instance which contains the services which have been registered up to that point:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

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

    // This will succeed.
    var fooService = sp.GetService<IFooService>();
    // This will fail (return null), as IBarService hasn't been registered yet.
    var barService = sp.GetService<IBarService>();
}

Please note: Generally you should avoid resolving services inside the ConfigureServices() method, as this is actually the place where you're configuring the application services. Sometimes you just need access to an IOptions<MyOptions> instance. You can accomplish this by binding the values from the IConfiguration instance to an instance of MyOptions (which is essentially what the options framework does):

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

Or use an overload for AddSingleton/AddScoped/AddTransient:

// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
    var fooService = sp.GetRequiredService<IFooService>();
    return new BarService(fooService);
}

Manually resolving services (aka Service Locator) is generally considered an anti-pattern. While it has its use-cases (for frameworks and/or infrastructure layers), you should avoid it as much as possible.


@HenkMollema but what if i can't have anything injected, I mean I can't have IServiceCollection injected, some class that is being created manually(out of middle ware scope), a scheduler in my case, which periodically needs some services to generate and send an email.
warning if you need to resolve services in ConfigureServices and that service is a singleton it will be a different singleton to the one your Controllers use! I assume this is because it uses a different IServiceProvider - to avoid this do NOT resolve via BuildServiceProvider and instead move your lookup of the singleton from ConfigureServices to Configure(..other params, IServiceProvider serviceProvider) in Startup.cs
@wal good point. Because it's a different IServiceProvider instance it will create a new singleton instance. You could avoid this by returning the service provider instance from the ConfigureServices method so that will be the container your application uses as well.
Invoking collection.BuildServiceProvider(); was what I needed, thanks!
@HenkMollema how do you make it work with only one service provider instance? Typically, you would 1) Register some of your dependencies 2) build an interim service provider instance 3) Use that service provider to resolve something you need to register some other dependencies. Afterwards, you can't return the interim instance, since it's missing some of your dependencies (registered in 3). Am I missing something?
M
Muhammad Rehan Saeed

Manually resolving instances involves using the IServiceProvider interface:

Resolving Dependency in Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

Resolving Dependencies in Startup.Configure

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

Resolving Dependencies in Startup.Configure in ASP.NET Core 3

public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    application.ApplicationServices.GetService<MyService>();
}

Using Runtime Injected Services

Some types can be injected as method parameters:

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

Resolving Dependencies in Controller Actions

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";

@AfsharMohebbi the GetService which is generic is an extension method in Microsoft.Extensions.DependencyInjection namespace.
About Extension Methods: An extension method is a static method that adds funcionality to a class, you could declare public static TheReturnType TheMethodName(this TheTypeYouExtend theTypeYouExtend { // BODY} and then you could use it like: TheTypeYouExtend.TheMethodName(); That has become a very common aproach with .NET Core, so that developers can extend base functionality... good examples here: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
B
BrunoLM

If you generate an application with a template you are going to have something like this on the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

You can then add dependencies there, for example:

services.AddTransient<ITestService, TestService>();

If you want to access ITestService on your controller you can add IServiceProvider on the constructor and it will be injected:

public HomeController(IServiceProvider serviceProvider)

Then you can resolve the service you added:

var service = serviceProvider.GetService<ITestService>();

Note that to use the generic version you have to include the namespace with the extensions:

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs (ConfigureServices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }

k
kilkfoe

If you just need to resolve one dependency for the purpose of passing it to the constructor of another dependency you are registering, you can do this.

Let's say you had a service that took in a string and an ISomeService.

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

When you go to register this inside Startup.cs, you'll need to do this:

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);

The OP didn't state the reason for needing to resolve a service in the ConfigureService method, but this is most likely the reason somebody would think to do that
In fact this should be the accepted answer... Although Henk Mollema's answer is very illustrative, nowadays your answer is cleaner and doesn't introduce problems related on building an intermediate IServiceProvider (different instances of singletons...). Probably, this solution wasn't available in 2015 when Henk anwered, but now it's the way to go.
Tried this but ISomeService was still null for me.
2 questions: 1) If the parameter constructor of service class AnotherService changes (removed or added services), then I need to modify the register segment of service IAnotherService and it keeps changing? 2) Instead, I can add only one constructor for AnotherService with 1 parameter like public AnotherService (IServiceProvider serviceProvider) and get services I need from the constructor. And I just need to register service class AnotherService in Startup class like services.AddTransient(sp => { var service = new AnotherService(sp); return service; });
services.AddScoped<IAnotherService>(ctx => ActivatorUtilities.CreateInstance<AnotherService>(ctx, "https://someservice.com/") ); should be the preferred solution.
B
Bora Aydın

You can inject dependencies in attributes like AuthorizeAttribute in this way

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));

This is what I was searching for.. Thanks
Thank you, Bora Aydın. Is there any downside to doing it this way versus constructor injection?
You can use this type of resolving where constructor injection is not available like attributes. As far as I know there is no downside of it. It is just another way of resolving dependencies.
This is they way to go if you are using IHttpContextAccessor as DI mixed with your repository.
Really good answer, just to add to this though - A more succinct way to write this is: var someservice = context.HttpContext.RequestServices.GetService<ISomeService>();
I
Izzy

I know this is an old question but I'm astonished that a rather obvious and disgusting hack isn't here.

You can exploit the ability to define your own ctor function to grab necessary values out of your services as you define them... obviously this would be ran every time the service was requested unless you explicitly remove/clear and re-add the definition of this service within the first construction of the exploiting ctor.

This method has the advantage of not requiring you to build the service tree, or use it, during the configuration of the service. You are still defining how services will be configured.

public void ConfigureServices(IServiceCollection services)
{
    //Prey this doesn't get GC'd or promote to a static class var
    string? somevalue = null;

    services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
         //create service you need
         var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
         //get the values you need
         somevalue = somevalue ?? service.MyDirtyHack();
         //return the instance
         return service;
    });
    services.AddTransient<IOtherService, OtherService>(scope => {
         //Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
         scope.GetService<IServiceINeedToUse>();
         //TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
         //Wow!
         return new OtherService(somevalue);
    });
}

The way to fix this pattern would be to give OtherService an explicit dependency on IServiceINeedToUse, rather than either implicitly depending on it or its method's return value... or resolving that dependency explicitly in some other fashion.


This is a great solution when you can't use the options pattern, like working with 3rd party libraries
@Rory I can only assure you there are more horrid hacks in the time since this post :'D
B
Bibin Gangadharan

You can inject dependencies using IApplicationBuilder instance in this way

public void Configure(IApplicationBuilder app)
    {
        //---------- Your code
        
        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var resultLogic = serviceScope.ServiceProvider.GetService<IResultLogic>();
            resultLogic.YourMethod();
        }           

        //---------- Your code
    }

u
user1437868

public void ConfigureServices(IServiceCollection services) {

        services.AddSingleton<ISelfServiceConfigLoad, SelfServiceConfigLoader>();
        var sp = services.BuildServiceProvider();
        var configservice = sp.GetServices<ISelfServiceConfigLoad>();
        services.AddSingleton<IExtractor, ConfigExtractor>( sp =>
        {
            var con = sp.GetRequiredService<ISelfServiceConfigLoad>();
             var config = con.Load();
            return new ConfigExtractor(config.Result);
        });
        services.AddSingleton<IProcessor<EventMessage>, SelfServiceProcessor>();          
        services.AddTransient<ISolrPush, SolrDataPush>();
        services.AddSingleton<IAPICaller<string, string>, ApiRestCaller<string, string>>();
        services.AddSingleton<IDataRetriever<SelfServiceApiRequest, IDictionary<string, object>>, SelfServiceDataRetriever>();
       


    }

How does that answer the question? Please provide some explaining text.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review
In the code snippet you gave you're registering services. But the question was, how to get an instance of any service inside of ConfigureServices?
He get the instance using the AddSingleton overload which gives access to serviceProvider. Instead of calling IServiceCollection.BuildServiceProvider() stackoverflow.com/q/31863981/423356 calling BuildServiceProvider() in ConfigureServices seems not best practice
W
Wai Ha Lee
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}

Your answers are more likely to get accepted and upvoted if you provide a brief explanation of why it is a good answer, not just a code snippet. It also helps the asker to be sure this is actually answering the question they asked.
Somebody incorrectly flagged your answer as low-quality. You should add some accompanying text to explain how your answer works to prevent further flagging and/or downvotes. A code-only answer is not low-quality. Does it attempt to answer the question? If not, flag as 'not an answer' or recommend deletion (if in the review queue). b) Is it technically incorrect? Downvote or comment. From review.