【问题标题】:How do you configure the DbContext when creating Migrations in Entity Framework Core?在 Entity Framework Core 中创建迁移时如何配置 DbContext?
【发布时间】:2015-05-20 12:57:17
【问题描述】:

在使用 Entity Framework 的迁移命令时,是否可以配置/引导依赖注入?

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

例如,以下将配置 EF 以使用从 config.json 检索的连接字符串持久保存到 SQL 服务器

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 现在我已经在 ASP.NET 5 中进行了一些开发(在传统的 .NET 应用程序中使用 EF7),我可以说围绕 Startup 类进行标准化对我来说听起来不错。
  • 我在aspnet/Hosting#286 中推动了它,但输了。决定是为不同的应用程序类型使用不同的 DbContext 加载器。见aspnet/EntityFramework#2357

标签: c# dependency-injection .net-core entity-framework-core dbcontext


【解决方案1】:

如果您正在寻找配置迁移上下文的解决方案,您可以在 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

【讨论】:

    【解决方案2】:

    正如@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 模块中。)

    【讨论】:

    • 您可以检查 optionsBuilder.IsConfigured 属性而不是 isMigration 标志。
    【解决方案3】:

    使用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&lt;TContext&gt; 现在是 obsolete

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

    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://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

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

    【讨论】:

    • 这个类现在已经过时了。
    • 您更新的解决方案与@Anton 的完全相同。它应该恢复到旧状态并保留用于历史目的。
    • 请注意 IDbContextFactory&lt;&gt; 已移至 efcore 5 中的 Microsoft.EntityFrameworkCore 命名空间并且不再过时:docs.microsoft.com/en-us/dotnet/api/…
    【解决方案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);
        }
    }
    

    【讨论】:

      【解决方案5】:

      结合上面的答案,这对我有用

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • 谢谢佩德罗,除非有什么改变,否则这在我的情况下是行不通的。我的应用程序使用的Startup 类不能在DbContext 子类中引用,因为它们存在于不同的程序集中。在此处和 Startup 中执行此配置不兼容。
        【解决方案7】:

        我只是要求一个实例并在我的 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”。如果它不存在,Migrate 应该创建它,尽管它应该如何找出我不知道的权限 - 所以我创建了一个空数据库。

        【讨论】:

        • 谢谢。在我的场景中,我在OnConfiguring 方法中有一个带有自定义配置的DbContext,我希望它生成多个数据库,所以我需要调用context.Database.Migrate 发布上下文配置。合适的地方在哪里?
        猜你喜欢
        • 1970-01-01
        • 2020-06-06
        • 1970-01-01
        • 2019-02-12
        • 2021-01-21
        • 2020-03-09
        • 2016-05-08
        • 2021-07-26
        • 2021-11-28
        相关资源
        最近更新 更多