EF migrations connection source by priority
There are many different ways for EF Core tools to get the connection string for the database to update using migrations. They are all listed in the documentation, but the order of priority in which they are used is unfortunately not very clear.
If your project implements the
IDesignTimeDbContextFactory<TContext>
interface for yourDbContext
, then theDbContext
returned by itsCreateDbContext
method is always going to be used for running the migrations. The connection string is usually going to be hardcoded in the method when using this approach:public class DesignTimeSampleDbContextFactory : IDesignTimeDbContextFactory<SampleDbContext> { public SampleDbContext CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<SampleDbContext>(); optionsBuilder.UseSqlServer( @"Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleFromFactory;Integrated Security=True" ); return new SampleDbContext(optionsBuilder.Options); } }
If there's no design-time factory class in your project, then the tools will attempt to get a
DbContext
instance from the applicationIHostBuilder
. It's the same approach as used by your application at run time. You register theDbContext
to be used with anAddDbContext
call. The connection string will usually be retrieved from a .NET configuration provider (e.g. read from a configuration file or environment variable):builder.Services.AddDbContext<SampleDbContext>(options => { options.UseSqlServer(builder.Configuration.GetConnectionString("Sample")); });
If you're not using the
IHostBuilder
or theDbContext
is not registered with it, then the tools will try to instantiate aDbContext
by calling its constructor with no parameters. In this case, you'll have to override itsOnConfiguring
method to set the connection string. If yourDbContext
also has a constructor with aDbContextOptions
parameter, you should add a check to theOnConfiguring
method to not override the connection string from there:protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseSqlServer( @"Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleFromOnConfiguring;Integrated Security=True" ); } }
In all three cases, you can override the connection string from the command line if you add the --connection
argument:
dotnet ef database update --connection 'Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleFromCommandLine;Integrated Security=True'
Of course, this will only work if the code creating the DbContext
instance to use does not throw an exception.
All the behavior described above remains the same even if you use the EF Core tools to build a bundle executable and run that instead of the dotnet ef database update
command. You can override the connection string with the --connection
argument when running the generated bundle executable:
dotnet ef migrations bundle
.\efbundle.exe --connection 'Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleFromCommandLine;Integrated Security=True'
If you want to try this out for yourself, check the sample project in my GitHub repository. I created it to verify if I interpreted the documentation correctly. The final commit uses the design-time factory, and the ones before that the other two approaches. I used a different database name in each connection string to easily check which database was created in my SQL Server LocalDB instance. I dropped the created database after each run to start with a clean state.
The EF Core tooling gives you a lot flexibility when setting up your process for running the migrations. And it's usually a part of the project you don't need to touch after the initial setup. Which gives you a lot of time to forget the details by the time you need to set up a new project. And then you end up wondering why it's not working as expected. Documentation can help, but in the end I had to create a dedicated sample project to fully understand the order of priority for all supported ways of creating a DbContext
at design time.