Ivan Ivanov
Ivan Ivanov

Reputation: 203

ServiceProvider.GetRequiredService vs ApplicationServies.GetRequiredService with a Singleton Service

What's the difference in calling singleton service.

In app.UseMvc();

app.UseMvc(options =>
{
    options
        .ServiceProvider
        .GetRequiredService<IYamlIndexer>()
        .IndexContentFiles(Constants.ContentPath);
});

Or this:

app
    .ApplicationServices
    .GetRequiredService<IYamlIndexer>()
    .IndexContentFiles(Constants.ContentPath);

Upvotes: 4

Views: 3686

Answers (2)

Shaun Luttin
Shaun Luttin

Reputation: 141512

Short Answer

For most use cases, there is not a difference between the two. Both properties point to the same IServiceProvider instance, and both will get the same instance of a required singleton service. In edge cases, the timing of the calls might be different, but I could not think of an edge case that would cause that to happen. Unless we're doing something unusual, both will run only once, and that will be during application startup.

Experiment to Demonstrate

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddSingleton<SomeSingltonService>();
    }

    public void Configure(IApplicationBuilder app)
    {
        var appServices = app.ApplicationServices;
        var appService = appServices.GetRequiredService<SomeSingltonService>();

        Console.WriteLine("=======================");
        Console.WriteLine("Configure");

        app.UseMvc(configureRoutes =>
        {
            var routeServices = routeBuilder.ServiceProvider;
            var routeService = routeServices.GetRequiredService<SomeSingltonService>();

            Console.WriteLine("UseMvc");

            if (appServices == routeServices && appService == routeService)
            {
                Console.WriteLine("They are the same instances.");
            }
        });

        Console.WriteLine("=======================");
    }
}

This is the output:

=======================        
Configure                      
UseMvc                         
They are the same instance.    
=======================        

Source Code to Demonstrate

Behind the scenes UseMvc passes the IApplicationBulder to the RouteBuilder constructor. Then the RouteBuilder assigns the IApplicationBulder.ApplicationServices to its own IRouteBuilder.ServiceProvider property.

Code from MvcApplicationBuilderExtensions.cs

public static IApplicationBuilder UseMvc(
    this IApplicationBuilder app,
    Action<IRouteBuilder> configureRoutes)
{
    // ...

    var routes = new RouteBuilder(app) // ln 136
    {
        DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
    };

    configureRoutes(routes);

    // ...
}

Code from RouteBuilder.cs

public RouteBuilder(IApplicationBuilder applicationBuilder, IRouter defaultHandler)
{
    // ...

    ServiceProvider = applicationBuilder.ApplicationServices; // ln 36

    // ...
}

Upvotes: 1

Dmitry
Dmitry

Reputation: 16795

In (2) IndexContentFiles will start immediately, during app startup, while application is not "fully running". This may cause some problems (depending of what IYamlIndexer.IndexContentFiles actually doing). Also, if this is synchronous long-running call - your app will be slow during startup.

In (1) this method will start.. at some time where MVC subsystem will ask for it options. This will occur somewhere after app startup, and again will take some time if it's long-running...

And question you should ask yourself - how Options are configured - singleton or transient (and why you trust this knowlege)? In worst case, your IndexContentFiles will be called each time MVC Options are requested and your app will die calling IndexContentFiles on every user request...

May be you need something like IApplicationLifetime?

Upvotes: 0

Related Questions