当在Entity Framework Core中创建迁移时,如何配置DbContext?

27

使用 Entity Framework 迁移命令时,是否有办法配置/引导依赖注入?

Entity Framework Core 支持为 DbContext 子类进行依赖注入。该机制包括允许在 DbContext 之外配置数据访问。

例如,下面的代码将配置 EF 以使用从 config.json 检索到的连接字符串将数据持久化到 SQL Server 中:

ServiceCollection services = ...

var configuration = new Configuration().AddJsonFile( "config.json" );
services.AddEntityFramework( configuration )
    .AddSqlServer()
    .AddDbContext<BillingDbContext>( config => config.UseSqlServer() );

然而,迁移命令不知道如何执行此代码,因此Add-Migration由于缺乏提供程序或连接字符串而失败。

可以通过在DbContext子类中覆盖OnConfiguring来指定提供程序和配置字符串,使迁移工作,但是当需要在其他地方使用不同的配置时,这会变得复杂。最终保持迁移命令和我的代码都正常工作变得令人不太满意。

注意:我的DbContext位于与使用它的入口点不同的程序集中,我的解决方案有多个启动项目。


弄清楚这个问题是#639的一部分。在ASP.NET 5中,我们将调用Startup.ConfigureServices()。您认为对于非ASP.NET 5项目使用相同的约定是否好? - bricelam
@bricelam现在我已经在ASP.NET 5中进行了一些开发(在传统的.NET应用程序中使用EF7),我可以说围绕“Startup”类进行标准化对我来说很有好处。 - vossad01
我在aspnet/Hosting#286上为此进行了推动,但失败了。决定是为不同的应用程序类型使用不同的DbContext加载器。请参见aspnet/EntityFramework#2357 - bricelam
8个回答

25
如果您正在寻找配置迁移上下文的解决方案,您可以在您的DBContext类中使用以下内容:
    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(connectionString);
        }
    }

记得安装这两个包以获得SetBasePathAddJsonFile方法:

Microsoft.Extensions.Configuration.FileExtensions

Microsoft.Extensions.Configuration.Json


7

使用 IDesignTimeDbContextFactory

如果在派生的DbContext所在的项目或应用程序启动项目中找到实现此接口的类,则工具将绕过其他创建DbContext的方式,而使用设计时工厂。

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);
        }
    }
}

应用于实体框架 2.0、2.1


使用 IDbContextFactory<TContext> 现在已经废弃

实现此接口以启用对没有公共默认构造函数的上下文类型的设计时服务。设计时服务将自动发现与派生上下文位于同一程序集中的此接口的实现。

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);
        }
    }
}

更多信息:https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

如果您不满意硬编码的连接字符串,请查看this文章。


1
这个类现在已经过时了。 - Shimmy Weitzhandler
您更新的解决方案与 @Anton 的完全相同。应将其还原回旧状态并保留作为历史记录。 - Daniel
1
请注意,IDbContextFactory<>在efcore 5中已经移动到Microsoft.EntityFrameworkCore命名空间中,并且不再过时:https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.idbcontextfactory-1?view=efcore-5.0 - ghord

6

@bricelam评论说,Entity Framework 7中还不存在这个功能。这个缺失的功能已经被GitHub问题aspnet/EntityFramework#639跟踪。

同时,我发现更容易的解决方法是利用全局状态而不是麻烦地进行子类化。虽然这通常不是我的首选设计选择,但现在它很有效。

在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>" );
    }
}

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() );
    // ...
}

(在我的情况下,配置代码实际上存储在Autofac模块中。)

6
您可以检查 optionsBuilder.IsConfigured 属性,而不是 isMigration 标志。 - Der_Meister

4
在.NET Core 2.1及以上版本中应使用IDesignTimeDbContextFactory,因为IDbContextFactory已经过时。
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);
    }
}

3

结合以上答案,这对我有用

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");
    }
}

1

我知道这是一个老问题,但我使用onConfiguring方法,我没有这个问题。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
}

2
谢谢Pedro,除非有什么变化,否则这在我的情况下行不通。我的应用程序使用的“Startup”类不能在“DbContext”子类中引用,因为它们位于不同的程序集中。在这里和“Startup”中进行此配置是不兼容的。 - vossad01

1

0
我只是在我的Startup.cs文件中请求一个实例并运行迁移。
  public void ConfigureServices(IServiceCollection services)
    {
        // ASPNet Core Identity
        services.AddDbContext<RRIdentityDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("RRIdentityConnectionString")));

     }

然后在配置文件中:

   public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        var rrIdentityContext = app.ApplicationServices.GetService<RRIdentityDbContext>();
        rrIdentityContext.Database.Migrate();
    }

注意:数据库中没有“EnsureCreated”方法。迁移应该在不存在时创建它,但是如何确定许可权限我不知道 - 因此我创建了一个空数据库。

谢谢。在我的场景中,我有一个带有自定义配置的 DbContextOnConfiguring 方法中,我想要它生成多个数据库,所以我需要在上下文配置后调用 context.Database.Migrate。哪里是正确的地方去做这件事? - Shimmy Weitzhandler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接