Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.InvalidOperationException: Unable to build IHost when creating EF Migration with Minimal API dotnet 6 #60891

Closed
IanBuck-dev opened this issue Oct 22, 2021 · 23 comments · Fixed by #61621
Labels
area-Extensions-Hosting enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@IanBuck-dev
Copy link

Description

I have an issue when creating ef migrations for the newest release candidate 6.0.0-rc.2.21480.5 for dotnet 6 with the minimal apis. It seems to be a similar issue as explained here dotnet/aspnetcore#33886

The timeout for creating a WebApplicationFactory at design time seems to be 5 sec and that is not enough for me when I add new migrations, because I need to setup a lot of configuration before. I get this exception when trying to run any command that requires the project to be build like dotnet ef migrations add AddedPlanningSessions -v and the DbContext to be fetched from the service provider. I would expect there to be no timeout at all or at least a very large one when using WebApplicationFactory at design time.

System.InvalidOperationException: Unable to build IHost
   at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost()
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args)
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass11_0.<ResolveServiceProviderFactory>b__3(String[] args)
   at Microsoft.EntityFrameworkCore.Design.Internal.AppServiceProviderFactory.CreateFromHosting(String[] args)

The issue can be easily reproduced by creating an empty project with minimal api dotnet new web -o service, adding a dbcontext to the DI and adding a sleep of 6 sec.

Include your code

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseSqlServer("localhost"));

Thread.Sleep(TimeSpan.FromSeconds(6));

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Include verbose output

Output when building my project where the steps until builder.Build() take too long.

dotnet exec --depsfile /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.deps.json --additionalprobingpath /Users/ian/.nuget/packages --additionalprobingpath /usr/local/share/dotnet/sdk/NuGetFallbackFolder --runtimeconfig /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.runtimeconfig.json /Users/ian/.dotnet/tools/.store/dotnet-ef/6.0.0-rc.2.21480.5/dotnet-ef/6.0.0-rc.2.21480.5/tools/netcoreapp3.1/any/tools/netcoreapp2.0/any/ef.dll dbcontext info --assembly /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.dll --project /Users/ian/Repos/work/planner-service/service/service.csproj --startup-assembly /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.dll --startup-project /Users/ian/Repos/work/planner-service/service/service.csproj --project-dir /Users/ian/Repos/work/planner-service/service/ --root-namespace PlannerService --language C# --framework net6.0 --nullable --working-dir /Users/ian/Repos/work/planner-service/service --verbose
Using assembly 'service'.
Using startup assembly 'service'.
Using application base '/Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0'.
Using working directory '/Users/ian/Repos/work/planner-service/service'.
Using root namespace 'PlannerService'.
Using project directory '/Users/ian/Repos/work/planner-service/service/'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'service'...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
Loading config from: global, planner-service.
System.InvalidOperationException: Unable to build IHost
   at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost()
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args)
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass11_0.<ResolveServiceProviderFactory>b__3(String[] args)
   at Microsoft.EntityFrameworkCore.Design.Internal.AppServiceProviderFactory.CreateFromHosting(String[] args)
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Unable to build IHost
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'PlannerContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'PlannerContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
 ---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[PlannerService.DAL.PlannerContext]' while attempting to activate 'PlannerService.DAL.PlannerContext'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(IServiceProvider provider, Type type)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass21_4.<FindContextTypes>b__13()
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass21_4.<FindContextTypes>b__13()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.GetContextInfo(String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.GetContextInfoImpl(String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.GetContextInfo.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to create an object of type 'PlannerContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

Include provider and version information

EF Core version: 6.0.0-rc.2.21480.5
Database provider: Microsoft.EntityFrameworkCore.SqlServer 6.0.0-rc.2.21480.5
Target framework: NET 6.0
Operating system:
IDE: VS Code Version: 1.61.1

@ajcvickers
Copy link
Member

/cc @davidfowl

@davidfowl
Copy link
Member

davidfowl commented Oct 22, 2021

This is a tough one as we need to tradeoff how much is too much time before we give up waiting for the host builder to be created... It's possible we need an environment variable override for these cases.

@ajcvickers Do you have a preference for where this gets fixed? I think we'd add support for an environment variable to override the timeout for cases like this. EF can provide a different timeout here

cc @maryamariyan @eerhardt

@ajcvickers
Copy link
Member

@davidfowl I don't have strong preferences. If you provide the mechanism, we can make use of it.

@davidfowl
Copy link
Member

https://github.com/dotnet/efcore/blob/8144d50ec0d15b87c35a9a3a425b1faadafb87f1/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs#L53

I can think of 2 things:

  • Make the default infinite for the EF tools, if it hangs then you can write out a message saying it should control+C (this is if somebody runs the tool on an app that is on .NET 6 but doesn't use the host at all). Treat it like an edge case.
  • Have an environment variable that the user can set.

@eerhardt
Copy link
Member

Is 5 seconds a reasonable default? Seems pretty low to me

@davidfowl
Copy link
Member

Yea, maybe it's too small. What should the default be?

@ajcvickers
Copy link
Member

What are the scenarios in which we expect this to timeout? I assume that it is when the application does not actually use hosting, right? But then, more specifically for EF, the question is whether or not we have to wait for the timeout if the EF model is discovered in some other way. For example, do we have to wait for the timeout if:

  • There is a DbContext with a parameterless constructor that we can use?
  • There is a DbContextFactory?

I think if those two cases work without waiting for the timeout, then having a longer timeout is okay, because we will only cause slowness in real negative cases where we are going to show an error anyway.

It seems like we need to do some investigation here into the user experience.

/cc @bricelam

@davidfowl
Copy link
Member

The timeout is only there for this edge case:

The user runs the EF tool on a .NET 6.0 application that references hosting but doesn't use it in the main entry point. Maybe its ok hanging in this scenario with a message on what the user can do?

@ajcvickers
Copy link
Member

@davidfowl In that case, it seems like you can just increase the timeout significantly. If all context discovery fails, then we will output:

Unable to create an object of type 'PlannerContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

As shown above.

@davidfowl
Copy link
Member

Right but it wouldn't show that message. It would just hang forever unless other changes were made to show the message after some timeout

@ajcvickers
Copy link
Member

@davidfowl I don't mean make the timeout infinite. I just mean increase it significantly, to say 20 or 30 seconds. It will then timeout and show the message. The user can then implement a design time context factory, in which case they can hopefully resolve the EF model quicker, or, at least, let it take as long as necessary without any timeout.

@davidfowl
Copy link
Member

Lets do that then. @eerhardt We should also change the default in the HostFactoryResolver as well (even though it's shared source).

@davidfowl
Copy link
Member

cc @captainsafia @maryamariyan

@eerhardt
Copy link
Member

Should this issue get moved to dotnet/runtime then? Or is there a change EF should make here as well?

@ajcvickers
Copy link
Member

@eerhardt As far as I know, it should move to runtime. EF will pick up the new source from there.

@davidfowl
Copy link
Member

Yep. Lets do that.

@eerhardt eerhardt transferred this issue from dotnet/efcore Oct 26, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added area-Extensions-Hosting untriaged New issue has not been triaged by the area owner labels Oct 26, 2021
@ghost
Copy link

ghost commented Oct 26, 2021

Tagging subscribers to this area: @eerhardt, @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

I have an issue when creating ef migrations for the newest release candidate 6.0.0-rc.2.21480.5 for dotnet 6 with the minimal apis. It seems to be a similar issue as explained here dotnet/aspnetcore#33886

The timeout for creating a WebApplicationFactory at design time seems to be 5 sec and that is not enough for me when I add new migrations, because I need to setup a lot of configuration before. I get this exception when trying to run any command that requires the project to be build like dotnet ef migrations add AddedPlanningSessions -v and the DbContext to be fetched from the service provider. I would expect there to be no timeout at all or at least a very large one when using WebApplicationFactory at design time.

System.InvalidOperationException: Unable to build IHost
   at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost()
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args)
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass11_0.<ResolveServiceProviderFactory>b__3(String[] args)
   at Microsoft.EntityFrameworkCore.Design.Internal.AppServiceProviderFactory.CreateFromHosting(String[] args)

The issue can be easily reproduced by creating an empty project with minimal api dotnet new web -o service, adding a dbcontext to the DI and adding a sleep of 6 sec.

Include your code

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseSqlServer("localhost"));

Thread.Sleep(TimeSpan.FromSeconds(6));

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Include verbose output

Output when building my project where the steps until builder.Build() take too long.

dotnet exec --depsfile /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.deps.json --additionalprobingpath /Users/ian/.nuget/packages --additionalprobingpath /usr/local/share/dotnet/sdk/NuGetFallbackFolder --runtimeconfig /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.runtimeconfig.json /Users/ian/.dotnet/tools/.store/dotnet-ef/6.0.0-rc.2.21480.5/dotnet-ef/6.0.0-rc.2.21480.5/tools/netcoreapp3.1/any/tools/netcoreapp2.0/any/ef.dll dbcontext info --assembly /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.dll --project /Users/ian/Repos/work/planner-service/service/service.csproj --startup-assembly /Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0/service.dll --startup-project /Users/ian/Repos/work/planner-service/service/service.csproj --project-dir /Users/ian/Repos/work/planner-service/service/ --root-namespace PlannerService --language C# --framework net6.0 --nullable --working-dir /Users/ian/Repos/work/planner-service/service --verbose
Using assembly 'service'.
Using startup assembly 'service'.
Using application base '/Users/ian/Repos/work/planner-service/service/bin/Debug/net6.0'.
Using working directory '/Users/ian/Repos/work/planner-service/service'.
Using root namespace 'PlannerService'.
Using project directory '/Users/ian/Repos/work/planner-service/service/'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'service'...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
Loading config from: global, planner-service.
System.InvalidOperationException: Unable to build IHost
   at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost()
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args)
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass11_0.<ResolveServiceProviderFactory>b__3(String[] args)
   at Microsoft.EntityFrameworkCore.Design.Internal.AppServiceProviderFactory.CreateFromHosting(String[] args)
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Unable to build IHost
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'PlannerContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'PlannerContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
 ---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[PlannerService.DAL.PlannerContext]' while attempting to activate 'PlannerService.DAL.PlannerContext'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(IServiceProvider provider, Type type)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass21_4.<FindContextTypes>b__13()
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass21_4.<FindContextTypes>b__13()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.GetContextInfo(String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.GetContextInfoImpl(String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.GetContextInfo.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to create an object of type 'PlannerContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

Include provider and version information

EF Core version: 6.0.0-rc.2.21480.5
Database provider: Microsoft.EntityFrameworkCore.SqlServer 6.0.0-rc.2.21480.5
Target framework: NET 6.0
Operating system:
IDE: VS Code Version: 1.61.1

Author: IanBuck-dev
Assignees: -
Labels:

untriaged, area-Extensions-Hosting

Milestone: -

@maryamariyan maryamariyan removed the untriaged New issue has not been triaged by the area owner label Oct 28, 2021
@maryamariyan maryamariyan added this to the 7.0.0 milestone Oct 28, 2021
@maryamariyan maryamariyan added the enhancement Product code improvement that does NOT require public API changes/additions label Oct 28, 2021
@maryamariyan
Copy link
Member

Next to do here based on earlier conversation

I just mean increase it significantly, to say 20 or 30 seconds.

is to increase timeout. On top of that we could consider adding an environment variable that could help developers set custom timeout

@ghorsey-opt
Copy link

ghorsey-opt commented Nov 11, 2021

I am running into this snag, getting the cannot create the migration:

System.InvalidOperationException: Unable to build IHost
   at Microsoft.Extensions.Hosting.HostFactoryResolver.HostingListener.CreateHost()
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass8_0.<ResolveHostFactory>b__0(String[] args)
   at Microsoft.Extensions.Hosting.HostFactoryResolver.<>c__DisplayClass11_0.<ResolveServiceProviderFactory>b__3(String[] args)
   at Microsoft.EntityFrameworkCore.Design.Internal.AppServiceProviderFactory.CreateFromHosting(String[] args)
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Unable to build IHost
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'QueryDbContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'QueryDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

My difference is I'm using Azure AppConfiguration to retrieve my dbconnections strings, etc

builder.Host
    // more before
    .ConfigureAppConfiguration(config =>
    {
        var settings = config.Build();
        var connection = settings.GetValue<string>("AppConfigConnectionString");
        config.AddAzureAppConfiguration(options =>
        {
            options
                .Connect(connection)
                .UseFeatureFlags(options => options.CacheExpirationInterval = TimeSpan.FromMinutes(30))
                .ConfigureKeyVault(kv =>
                {
                    kv.SetCredential(new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true }))
                        .SetSecretRefreshInterval(TimeSpan.FromHours(24));
                })
                .ConfigureRefresh(refresh =>
                {
                    refresh
                        .Register("i12n:SentinelKey", refreshAll: true)
                        .SetCacheExpiration(TimeSpan.FromHours(4));
                });
        });

        config.AddJsonFile("appsettings.Development.json", optional: true);
    })
    // more after

If I remove the .ConfigureAppConfiguration call and just use hardcoded connection string in my app secrets, everything works fine.

@davidfowl
Copy link
Member

@ghorsey-opt this won't help but you can remove ConfigureAppConfiguration and just use builder.Configuration to clean up the code a bit

@ghorsey-opt
Copy link

yeah, I think I need to come up with an alternative to generate the migrations., All the connection strings from keyvault and configurations and feature flags from AppConfiguration are needed for the app.

I'm toying around with the idea of creating a separate console app that has the configuration for the dbcontext and connection information locally in user secrets, then using that project as the startup project to generate the migrations... feels like a lot of extra work though.

@davidfowl
Copy link
Member

We're gonna fix the bug in a 6.0.x patch.

@maryamariyan maryamariyan modified the milestones: 7.0.0, 6.0.x Nov 12, 2021
@halter73
Copy link
Member

I think the main thing we need to do is improve the exception message. System.InvalidOperationException: Unable to build IHost does not make it clear there was a 5 second timeout waiting for Program.Main() to actually build the IHost. Adding remediation options to the exception message like setting a new environment variable to increase the timeout will help a lot.

I'm not sure most users want to wait 20-30 seconds to load the IHost for EF migrations. I get that's the max before it times out, but it would be nice to let developers know that reducing the time spent in Program.Main() before building the IHost will speed up migrations rather than leave people to wonder why migrations are now slower.

Maybe we could consider adding a command line argument to indicate that it's being resolved by the HostFactoryResolver so developers can short-circuit unnecessary startup logic when just setting up DB services for doing migrations. We already add --applicationName, so maybe the exception message could reference that.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Nov 16, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Nov 17, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Dec 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-Hosting enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants