问题
Is there way that dependency injection can be configured/bootstrapped when using Entity Framework's migration commands?
Entity Framework Core supports dependency injection for DbContext
subclasses. This mechanism includes allowing for configuration of data access outside of of the DbContext
.
For example, the following would configure EF to persist to a SQL server using a connection string retrieved from config.json
ServiceCollection services = ...
var configuration = new Configuration().AddJsonFile( "config.json" );
services.AddEntityFramework( configuration )
.AddSqlServer()
.AddDbContext<BillingDbContext>( config => config.UseSqlServer() );
However, the migrations commands do not know to execute this code so Add-Migration
will fail for lack of a provider or lack of a connection string.
Migrations can be made to work by overriding OnConfiguring
within the DbContext
subclass to specify the provider and configuration string, but that gets in the way when different configuration is desired elsewhere. Ultimately keeping my the migration commands and my code both working becomes undesirably complex.
Note: My DbContext
lives in a different assembly than the entry point that uses it and my solution has multiple start-up projects.
回答1:
As @bricelam commented this functionality does not yet exist in Entity Framework 7. This missing functionality is tracked by GitHub issue aspnet/EntityFramework#639
In the mean time, the easier workaround I found was to utilize a global state rather than hassle with subclassing. Not usually my first design choice but it works well for now.
In MyDbContext:
public static bool isMigration = true;
protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder )
{
// TODO: This is messy, but needed for migrations.
// See https://github.com/aspnet/EntityFramework/issues/639
if ( isMigration )
{
optionsBuilder.UseSqlServer( "<Your Connection String Here>" );
}
}
In Startup.ConfigureServices()
.
public IServiceProvider ConfigureServices( IServiceCollection services )
{
MyContext.isMigration = false;
var configuration = new Configuration().AddJsonFile( "config.json" );
services.AddEntityFramework( configuration )
.AddSqlServer()
.AddDbContext<MyDbContext>( config => config.UseSqlServer() );
// ...
}
(The configuration code actually lives in an Autofac Module in my case.)
回答2:
Use IDesignTimeDbContextFactory
If a class implementing this interface is found in either the same project as the derived DbContext
or in the application's startup project, the tools bypass the other ways of creating the DbContext
and use the design-time factory instead.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Data Source=blog.db");
return new BloggingContext(optionsBuilder.Options);
}
}
}
applied in Entity Framework 2.0, 2.1
Using IDbContextFactory<TContext>
is now obsolete.
Implement this interface to enable design-time services for context types that do not have a public default constructor. Design-time services will automatically discover implementations of this interface that are in the same assembly as the derived context.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDbContextFactory<BloggingContext>
{
public BloggingContext Create()
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer("connection_string");
return new BloggingContext(optionsBuilder.Options);
}
}
}
more info : https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
If you're not happy with the hard-coded connection-string, take a look at this article.
回答3:
I know this is a old question but I use the onConfiguring method and I don't have this problem
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
}
回答4:
To combine the answers above this works for me
private readonly bool isMigration = false;
public MyContext()
{
isMigration = true;
}
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (isMigration)
{
optionsBuilder.UseSqlServer("CONNECTION_STRING");
}
}
回答5:
In .NET Core since version 2.1 should be used IDesignTimeDbContextFactory
because IDbContextFactory
is obsolete.
public class FooDbContextFactory : IDesignTimeDbContextFactory<FooDbContext>
{
public FooDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<FooDbContext>();
var connectionString = configuration.GetConnectionString("ConnectionStringName");
builder.UseSqlServer(connectionString);
return new FooDbContext(builder.Options);
}
}
回答6:
I just ask for an instance and run migrations in my Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
// ASPNet Core Identity
services.AddDbContext<RRIdentityDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("RRIdentityConnectionString")));
}
And then in Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var rrIdentityContext = app.ApplicationServices.GetService<RRIdentityDbContext>();
rrIdentityContext.Database.Migrate();
}
Note: There is no 'EnsureCreated' for the database. Migrate is supposed to create it if it doesn't exist, although how it is supposed to figure out the permissions I don't know - so I created an empty database.
回答7:
If you are looking for a solution to configure cotext for imgrations, you can use this in you DBContext
class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("DbCoreConnectionString");
optionsBuilder.UseSqlServer("Data Source=localhost;Initial Catalog=aspnetcore;Integrated Security=True");
}
}
Remember to install those two packages to have SetBasePath
and AddJsonFile
methods:
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
来源:https://stackoverflow.com/questions/29110241/how-do-you-configure-the-dbcontext-when-creating-migrations-in-entity-framework