6

I am building an application in ASP.NET Core 2.0 and I am having problems with EntityFramework Migrations.

I have my DbContext in a separate project (SolutionName\ProjectNamePrefix.Data) and therefore I created an implementation for the IDesignTimeDbContextFactory interface.

I wanted to use different connection strings for different environments and I need appsettings.json for that.

So after a quick search I found that I can create a new IConfigurationRoot object inside the CreateDbContext function as shown here: https://codingblast.com/entityframework-core-idesigntimedbcontextfactory/

I added that and then for testing, tried to run dotnet ef migrations list -c MyContext from the Data project root folder.

Then I got the following error:

The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\dev\*SolutionName*\*ProjectNamePrefix*.Data\bin\Debug\netcoreapp2.0\appsettings.json'.

So, basically, I tried 3 options for getting the correct root path:

and all of them returned the same ..\bin\debug\netcoreapp2.0\ path. When I run the Data project from VS, then the two first options give me the correct project root folder.

Is there a way to get the correct project content root folder?

Because when I added --verbose to the EF command, it logged out a row:

Using content root 'C:\dev\FitsMeIdentity\FitsMeIdentity.Data\'.

So I understand that EF somehow knows the project root but all the options mentioned above return the path for the already built application.

The only option I found that works is that I change Copy output to root folder to Copy always but found from here: https://www.benday.com/2017/02/17/ef-core-migrations-without-hard-coding-a-connection-string-using-idbcontextfactory/ that it's not a good idea.

At first I even thought about creating a Constructor for the IDesignTimeDbContextFactory implementation which gets IOptions as a parameter but that didn't work, had the same problem as explained here: Injecting Env Conn String into .NET Core 2.0 w/EF Core DbContext in different class lib than Startup prj & implementing IDesignTimeDbContextFactory

V. Samma
  • 2,558
  • 8
  • 30
  • 34

2 Answers2

13

A little late, but here is the solution for those who hate hard-coding connections strings:

internal class MigrationDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
    public AppDbContext CreateDbContext(string[] args)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", false)
            .Build();
        
        string connectionString = configuration.GetConnectionString("DefaultConnection");

        DbContextOptionsBuilder<AppDbContext> optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
        optionsBuilder.UseMySql(connectionString,
            ServerVersion.AutoDetect(connectionString),
            mySqlOptions =>
                mySqlOptions.EnableRetryOnFailure(
                    maxRetryCount: 10,
                    maxRetryDelay: TimeSpan.FromSeconds(30),
                    errorNumbersToAdd: null));

        return new AppDbContext(optionsBuilder.Options);
    }
}
Sean Thorburn
  • 1,728
  • 17
  • 31
1

No. You can't do this, and more to the point: you're not supposed to do this. The whole entire point of IDesignTimeDbContextFactory is that it's a way to get a DbContext instance from in a context where there is no ASP.NET Core framework to work with, i.e. from a class library. If you're running migrations from an ASP.NET Core project, you don't need it, and if you're not, none of the configuration stuff is available.

Additionally, it's only to be used for development, hence the "DesignTime" part of the name. As a result, there's no need for stuff like switching between connection strings for different environments. Just hard-code the connection string as the docs detail.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Thanks for the quick answer. Okay, maybe I'm missing something from the big picture then. What is the intended way to apply EF migrations to Staging/Production environments? I shouldn't use `Update-Database` but instead should I use `Script-Migration` to get the SQL scripts and run them manually every time? – V. Samma Nov 01 '17 at 14:32
  • 1
    Yep. That, or some other method of creating versioned migrations, such as ReadyRoll or similar products. You could also create a database project instead. Long and short, there's many different ways to get database changes to production. Just find something that works for your needs. – Chris Pratt Nov 01 '17 at 14:46
  • 1
    I'm confused. I thought that Entity Framework is a tool for end-to-end database management. Quoting [this](https://stackoverflow.com/a/29755781/1616049): `What's the point of this easy to use system if you can't take it all the way?` It doesn't make sense to me to use a third party tool for production DB deployment. Or at least not paid tools. And it's explicitly said on ReadyRoll's website that: `Please note that ReadyRoll's integration with Entity Framework CodeFirst is evolving and may not be suitable for production use.` I will look into DbUp meanwhile but it seems to be added complexity. – V. Samma Nov 02 '17 at 08:29
  • Entity Framework is just an ORM. It is not and never claimed to be a be all, end all, end to end solution for everything you'd ever need to do with a database. Production deployment is a *process*. There should be change management policies and in most Enterprise situations, you'd never be able to get a EF migration within spitting distance of a production database anyways. – Chris Pratt Nov 02 '17 at 12:11
  • You can access `appsettings.json` configuration in your `IDesignTimeDbContextFactory` implementation this way - see my answer for [Injecting Env Conn String into .NET Core 2.0 w/EF Core DbContext in different class lib than Startup prj & implementing IDesignTimeDbContextFactory](https://stackoverflow.com/a/53924899/804385) – Dmitry Pavlov Dec 26 '18 at 14:55