efcore: EF 3.1 Add-Migration failing to create new migration with -StartupProject, but EF 2.2 works

I’m currently upgrading my project Speedy from EF core 2.2 to 3.1. The command below works fine on 2.2 but fails on 3.1.

Add-Migration Initial -Project Speedy.Website.Data.Sql -StartupProject Speedy.Website -Verbose

In the Speedy repository https://github.com/BobbyCannon/Speedy there are two master branches.

  • master == EF Core 3.1
  • master-core-2 == EF Core 2.2

Steps to reproduce

The code below returns null when using EF Core 3.1 but returns the connection string in the web.config of Speedy.Website.

Both projects reference System.Configuration.ConfigurationManager 4.7.0 nuget package to access the connection string in the web.config.

var connection = ConfigurationManager.ConnectionStrings["DefaultConnection"];
if (connection != null && !string.IsNullOrWhiteSpace(connection.ConnectionString))
{
	return connection.ConnectionString;
}

Further technical details

EF Core version: 2.2, 3.1 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET FX 4.7.2 Operating system: Windows 10 IDE: Visual Studio 2019 16.4

Output for EF Core 2.2

PM> Add-Migration Initial -Project Speedy.Website.Data.Sql -StartupProject Speedy.Website -Verbose Using project ‘Speedy.Website.Data.Sql’. Using startup project ‘Speedy.Website’. Build started… Build succeeded. C:\Workspaces\GitHub\Speedy - Core 2.2\packages\Microsoft.EntityFrameworkCore.Tools.2.2.6\tools\net461\any\ef.exe migrations add Initial --json --verbose --no-color --prefix-output --assembly “C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\bin\Speedy.Website.Data.Sql.dll” --startup-assembly “C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\bin\Speedy.Website.dll” --project-dir “C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website.Data.Sql\” --language C# --working-dir “C:\Workspaces\GitHub\Speedy - Core 2.2” --data-dir “C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\App_Data” --root-namespace Speedy.Website.Data.Sql Using assembly ‘Speedy.Website.Data.Sql’. Using startup assembly ‘Speedy.Website’. Using application base ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\bin’. Using working directory ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website’. Using root namespace ‘Speedy.Website.Data.Sql’. Using project directory 'C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website.Data.Sql'. Using configuration file ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\bin\Speedy.Website.dll.config’. Using data directory ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website\App_Data’. Finding DbContext classes… Finding IDesignTimeDbContextFactory implementations… Finding application service provider… Finding IWebHost accessor… No entry point was found for assembly ‘Speedy.Website’. No application service provider was found. Finding DbContext classes in the project… Found DbContext ‘ContosoSqlDatabase’. Using context ‘ContosoSqlDatabase’. Finding design-time services for provider ‘Microsoft.EntityFrameworkCore.SqlServer’… Using design-time services from provider ‘Microsoft.EntityFrameworkCore.SqlServer’. Finding design-time services referenced by assembly ‘Speedy.Website’. No referenced design-time services were found. Finding IDesignTimeServices implementations in assembly ‘Speedy.Website’… No design-time services were found. DetectChanges starting for ‘ContosoSqlDatabase’. DetectChanges completed for ‘ContosoSqlDatabase’. Writing migration to ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website.Data.Sql\Migrations\20200131123936_Initial.cs’. Writing model snapshot to ‘C:\Workspaces\GitHub\Speedy - Core 2.2\Speedy.Website.Data.Sql\Migrations\ContosoSqlDatabaseModelSnapshot.cs’. ‘ContosoSqlDatabase’ disposed. To undo this action, use Remove-Migration. PM>

Output for EF Core 3.1

PM> Add-Migration Initial -Project Speedy.Website.Data.Sql -StartupProject Speedy.Website -Verbose Using project ‘Speedy.Website.Data.Sql’. Using startup project ‘Speedy.Website’. Build started… Build succeeded. C:\Workspaces\GitHub\Speedy\packages\Microsoft.EntityFrameworkCore.Tools.3.1.1\tools\net461\any\ef.exe migrations add Initial --json --verbose --no-color --prefix-output --assembly C:\Workspaces\GitHub\Speedy\Speedy.Website\bin\Speedy.Website.Data.Sql.dll --startup-assembly C:\Workspaces\GitHub\Speedy\Speedy.Website\bin\Speedy.Website.dll --project-dir C:\Workspaces\GitHub\Speedy\Speedy.Website.Data.Sql\ –language C# --working-dir C:\Workspaces\GitHub\Speedy --data-dir C:\Workspaces\GitHub\Speedy\Speedy.Website\App_Data --root-namespace Speedy.Website.Data.Sql Using assembly ‘Speedy.Website.Data.Sql’. Using startup assembly ‘Speedy.Website’. Using application base ‘C:\Workspaces\GitHub\Speedy\Speedy.Website\bin’. Using working directory ‘C:\Workspaces\GitHub\Speedy\Speedy.Website’. Using root namespace ‘Speedy.Website.Data.Sql’. Using project directory 'C:\Workspaces\GitHub\Speedy\Speedy.Website.Data.Sql'. Using configuration file ‘C:\Workspaces\GitHub\Speedy\Speedy.Website\bin\Speedy.Website.dll.config’. Using data directory ‘C:\Workspaces\GitHub\Speedy\Speedy.Website\App_Data’. Using assembly ‘Speedy.Website.Data.Sql’. Using startup assembly ‘Speedy.Website’. Using application base ‘C:\Workspaces\GitHub\Speedy\Speedy.Website\bin’. Using working directory ‘C:\Workspaces\GitHub\Speedy\Speedy.Website’. Using root namespace ‘Speedy.Website.Data.Sql’. Using project directory 'C:\Workspaces\GitHub\Speedy\Speedy.Website.Data.Sql'. Using data directory ‘C:\Workspaces\GitHub\Speedy\Speedy.Website\App_Data’. Finding DbContext classes… Finding IDesignTimeDbContextFactory implementations… Finding application service provider… Finding Microsoft.Extensions.Hosting service provider… No static method ‘CreateHostBuilder(string[])’ was found on class ‘Program’. No application service provider was found. Finding DbContext classes in the project… Found DbContext ‘ContosoSqlDatabase’.

**** Connection String:

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 18 (6 by maintainers)

Most upvoted comments

EDIT: The underlying issue causing this bug (https://github.com/dotnet/runtime/issues/931) has been fixed in .NET 6 (relevant PR: https://github.com/dotnet/runtime/pull/56748), making the workaround below unnecessary if you update to .NET 6.


This workaround may also be useful for anyone porting an application from .NET Framework to .NET Core and isn’t ready to switch to Microsoft.Extensions.Configuration.

It is mostly the same as the previous suggestion, but instead of setting APP_CONFIG_FILE (which has no effect in .NET Core), the config file is copied to the default expected location before resetting the configuration system. For example, when running with the dotnet ef CLI tools, the configuration system expects a file named “ef.dll.config”.

class MyDesignTimeContextFactory : IDesignTimeDbContextFactory<MyContext>
{
    public MyContext CreateDbContext(string[] args)
    {
        // Copy the app config file to the expected location (i.e. "ef.dll.config")
        // See: https://github.com/dotnet/corefx/blob/v3.1.9/src/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs#L63
        var mainProjectAssembly = Assembly.Load("MyApp");  // TODO: Update this
        string mainProjectConfigPath = new Uri(mainProjectAssembly.CodeBase).LocalPath + ".config";
        var entryAssembly = Assembly.GetEntryAssembly();
        string newConfigPath = new Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, entryAssembly.ManifestModule.Name)).LocalPath + ".config";
        File.Copy(mainProjectConfigPath, newConfigPath, overwrite: true);

        // Reset the configuration system
        typeof(ConfigurationManager)
            .GetField("s_initState", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, 0);
        typeof(ConfigurationManager)
            .GetField("s_configSystem", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, null);
        typeof(ConfigurationManager).Assembly
            .GetType("System.Configuration.ClientConfigPaths")
            .GetField("s_current", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, null);

        return new MyContext();
    }
}

Workaround

You can work around this issue by adding an implementation of IDesignTimeDbContextFactory that updates the config file location:

class MyDesignTimeContextFactory : IDesignTimeDbContextFactory<MyContext>
{
    public MyContext CreateDbContext(string[] args)
    {
        // Work around dotnet/efcore#19760
        var startupAssembly = Assembly.Load("MyApp"); // TODO: Update this
        AppDomain.CurrentDomain.SetData(
            "APP_CONFIG_FILE",
            new Uri(startupAssembly.CodeBase).LocalPath + ".config");
        typeof(ConfigurationManager)
            .GetField("s_initState", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, 0);
        typeof(ConfigurationManager)
            .GetField("s_configSystem", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, null);
        typeof(ConfigurationManager).Assembly
            .GetType("System.Configuration.ClientConfigPaths")
            .GetField("s_current", BindingFlags.Static | BindingFlags.NonPublic)
            .SetValue(null, null);

        return new MyContext();
    }
}

We have experienced the exact same problem as @BobbyCannon