Trying to add migrations to an EF7 Model that lives in an ASP.NET 5 class library. When running dnx . ef migration add mymigration
fails with different results depending on which project I run it on.
If I run it in the folder of the main project, it cannot locate a DbContext
, this makes sense because the DbContext
is in the shared project and ef commands likely don't care about dependencies.
If I run it in the folder of the shared project, it does not have access to the connection string specified in the startup.cs. I have gleaned from questions like this that it does work from the shared project if you specify the connection string in the OnConfiguring
method of the DbContext
but I really would like to keep this code separate from the configuration.
I came across some issue logs in the EF7 repository that mention they implemented command line options for specifying a project and a context but there are no examples and I couldn't figure out how to use it from looking at the source code in the commit history.
Here is an approach that might work for you.
If I run it in the folder of the shared project, it does not have access to the connection string specified in the startup.cs.
Startup.cs
I'm assuming that in your Startup.cs, you're specifying the connection string by accessing Configuration
rather than by hard coding it. Further, I'm assuming that in your Startup.cs file's constructor, you're setting up configuration from a few sources. In other words, your Startup.cs might look something like this:
public class Startup
{
public IConfiguration Config { get; set; }
public Startup(IHostingEnvironment env)
{
var config = new Configuration()
.AddJsonFile("config.json")
.AddUserSecrets()
.AddEnvironmentVariables();
Config = config;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(Config["ConnectionStrings:MyDbContext"]);
});
}
public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider)
{
var db = serviceProvider.GetRequiredService<MyDbContext>();
db.Database.AsSqlServer().EnsureCreated();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
config.json
Further, I'm assuming that you're adding the connection string to a config.json in the root of your project (or that you adding it via user secrets or environmental variables.) Your config.json might look something like this:
{
"ConnectionStrings": {
"MyDbContext": "Some-Connection-String"
}
}
If you're not doing it that way, it might be worth trying it.
I have gleaned from questions like this that it does work from the shared project if you specify the connection string in the OnConfiguring method of the DbContext but I really would like to keep this code separate from the configuration.
DbContext
If my assumptions above are correct, then you can access the connection string in the DbContext
by using the same pattern that you used in the Startup
class. That is, in the DbContext
constructor, setup the IConfiguration
. Then, in OnConfiguring
, access the connection string. It might look something like this:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<SomeModel>().Key(e => e.Id);
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString = Config["ConnectionStrings:MyDbContext"];
optionsBuilder.UseSqlServer(connString);
}
public IConfiguration Config { get; set; }
public MyDbContext()
{
var config = new Configuration()
.AddJsonFile("config.json")
.AddEnvironmentVariables();
Config = config;
}
}
Project Structure
You'll of course need to have a config.json file in the root of your shared projects folder too. So, your projects' structure could look something like this:
SharedDataContext
Migrations
config.json
project.json
WebApp
config.json
project.json
Startup.cs
In the above, both config.json files contain a connection string setting for the DbContext
.
Some Thoughts
If you don't like duplicating the config.json connection string stuff, then you can use environmental variables or user secrets instead.