54

I need to add a couple of await functions in ConfigureServices in Startup.cs and am running into an issue.

System.InvalidOperationException Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddMvc()' inside the call to 'IApplicationBuilder.ConfigureServices(...)' or 'IApplicationBuilder.UseMvc(...)' in the application startup code.

As seen by the code below, AddMvc & UseMvc are in their correct locations, however, I still get this error.

public async void ConfigureServices(IServiceCollection services)
{
    ...
    await manager.Initialize();
    var data = await manager.GetData();
    ...
    services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory)
{
    ...
    app.UseMvc();
    ....
}

Is it possible to make ConfigureServices an async function?

silkfire
  • 24,585
  • 15
  • 82
  • 105
Brian Vu
  • 633
  • 1
  • 5
  • 8
  • Similar question with an actual answer that isn't just 'No!' ;-) https://stackoverflow.com/questions/56077346/asp-net-core-call-async-init-on-singleton-service – Simon_Weaver Jun 26 '19 at 21:49

2 Answers2

61

No, you can't. Doing that would result in a race condition.

Instead, consider making your operation synchronous or using .Wait()/.Result (depending on whether the async method returns data or not) to block until the asynchronous task completes.

Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
  • 43
    Can you explain why does the race condition occur? A bit more depth here with better understanding of what's executing when would be terrific. – Mavi Domates Mar 29 '19 at 10:24
  • 3
    Please add some explanation about the "race condition" – Vencovsky Aug 31 '20 at 14:53
  • 3
    @MaviDomates, @Vencovsky, the race condition occurs because the underlying code within the `ConfigureServices` of `IHostBuilder` continues to process the remainder of the host startup without awaiting your specific async implementation. This is because `ConfigureServices` accepts an `Action` delegate which returns no `Task` or anything that's awaitable. As far as the builder is concerned it has executed your method and done its job. – ColinM Sep 23 '20 at 13:42
  • Do not use `Wait()` or `Result()`. Atleast use `GetAwaiter` or `GetResult` if you feel you must. But in general it is bad practice because you risk deadlocks or starvation of the thread pool. [link](https://gsferreira.com/archive/2020/08/avoid-getawaiter-getresult-at-all-cost/) – Nulle Aug 26 '21 at 08:08
  • 1
    Pretty sure GetAwaiter() just wraps the task in an awaiter and GetResult() on the awaiter just calls the Result property which will call Wait() on the task if it isn't finished... The only real benefit is how exception propagate. GetAwaiter() isn't actually intended for you to use (ref: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.taskawaiter?view=netstandard-2.0) – Sinaesthetic Sep 10 '21 at 22:48
  • i.e. Both synchronous approaches are prone to deadlock. – Sinaesthetic Sep 10 '21 at 22:57
  • 1
    This also appears to be netcore web application/api. I'm fairly certain deadlocks are of no concern here because there's no context. – Sinaesthetic Sep 10 '21 at 23:07
2

If you just need to perform asynchronous actions on the services from the ServiceProvider before starting the application, then there is a simple way to do it using the HostInitActions nuget

    public void ConfigureServices(IServiceCollection services)
    {       
        services.AddSingleton<IService, MyService>();

        services.AddAsyncServiceInitialization()
            .AddInitAction<IService>(async (service) =>
            {
                await service.InitAsync();
            });
    }

This nugget ensures that your initialization action will be performed asynchronously before the application starts but right after the ServiceProvider is built, so the services are already available using dependency injection. These asynchronous operations can have multiple services as input, and thanks to this, they can also cooperate and share the data they obtained as part of this asynchronous action. Just look at the nuget documentation.

Another advantage of this approach is that this initialization action can be defined from any place where services are installed into the IServiceCollection (For example, in an extension method in another project that installs internal implementations of public interfaces). This means that the ASP.NET Core project does not need to know what service and how it should be initialized, and it will still be done.

  • 1
    This seems to be build on top of `Microsoft.Extensions.Hosting`, which basically just calls `StartAsync().GetAwaiter().GetResult()`. It's cleaner in that it adds centralization, but it's still a bit cheaty. – Brian Mar 17 '23 at 19:35