How to Dependency Inject a Service from Startup back in Program

Full source code here.

While writing some recent blog posts on HttpClientFactory I had to work with some of the obscure features of ServiceCollection and dependency injection in .NET Core 2.1. One of the things I wanted to do was get at an instance of a token generator from the service collection inside the Main method in Program.cs.

Some time ago I wrote a post about using the ServiceCollection from Program.cs and while that worked for the example I gave, there are cases where it would not depending on the specifics of what scope you are using, and in the case of a singleton, how you instanced it. When it goes wrong you end up with multiple instances of a singleton, multiple DI containers and plenty of unpredictable results. It’s too painful to explain all the variations, you can read a bit more here where David Fowler says “hosting current[ly] creates 3 service providers and its extremely broken in the general case”.

How to Pass a Service from ServicesCollection into Program

In this approach none there are none of the problems outlined above, only one instance of the singleton is every instantiated.

In Startup.cs add all the services you want inside ConfigureServices(..).

1public void ConfigureServices(IServiceCollection services)
2{
3    services.AddMemoryCache();
4    services.AddSingleton<ITokenGenerator, TokenGenerator>();
5}

In Program.cs, I have the usual CreateWebHostBuilder(..), no changes.

1public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
2    WebHost.CreateDefaultBuilder(args)	
3        .UseStartup<Startup>();	

But in Main, instead of

CreateWebHostBuilder(args).Build().Run();

I split building the WebHost from running the WebHost.

1public static void Main(string[] args)
2{		
3    IWebHost webHost = CreateWebHostBuilder(args).Build();
4    var tokenGenerator = webHost.Services.GetService<ITokenGenerator>();
5    string token =  tokenGenerator.GetToken();
6    System.Console.WriteLine(token);
7    webHost.Run();
8}
- Line 3 creates the `webHost`, **but doesn't run it**

this in turn instantiates the startup class and runs ConfigureServices(..)

- Line 4 reaches into the `ServiceCollection` for the `ITokenGenerator` service that was setup in `ConfigureServices(..)`
- Lines 5 and 6, use the `TokenGenerator` service to get a token a print it to screen
- Line 7 runs the `WebHost` and the application starts up

 
Full source code here.

comments powered by Disqus

Related