【问题标题】:Entity Framework Core migration - connection stringEntity Framework Core 迁移 - 连接字符串
【发布时间】:2018-05-08 11:15:21
【问题描述】:

我在处理数据库连接字符串和迁移时遇到了问题。 我有 2 个项目:

  • 应用

DbContext 在 Domain 项目中,所以这是我运行迁移的项目。 迁移概念强制我在 DbContext 中实现 OnConfiguring,并在其中指定数据库提供程序,例如:

protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
    builder.UseSqlServer("<connection string>");
}

我的问题是我不想使用硬编码的连接字符串,原因很明显,而且我不能使用 ConfigurationManager 从配置文件中读取它,因为配置文件在应用程序项目中。

【问题讨论】:

    标签: c# .net-core entity-framework-core entity-framework-migrations


    【解决方案1】:

    我见过的所有示例都涉及对连接字符串进行硬编码或将其放入我的 ASP.NET Core 应用程序的设置文件中。

    如果您不使用 ASP.NET Core,或者我不知道,不想将本地环境的数据库详细信息提交给源代码管理,您可以尝试使用临时环境变量。

    首先,像这样实现IDesignTimeDbContextFactory(注意IDbContextFactory 现在已弃用):

    public class AppContextFactory: IDesignTimeDbContextFactory<AppContext>
    {
        public AppContextFactory()
        {
            // A parameter-less constructor is required by the EF Core CLI tools.
        }
    
        public AppContext CreateDbContext(string[] args)
        {
            var connectionString = Environment.GetEnvironmentVariable("EFCORETOOLSDB");
            if (string.IsNullOrEmpty(connectionString))
                throw new InvalidOperationException("The connection string was not set " +
                "in the 'EFCORETOOLSDB' environment variable.");
    
             var options = new DbContextOptionsBuilder<AppContext>()
                .UseSqlServer(connectionString)
                .Options;
            return new AppContext(options);
        }
    }
    

    然后,您可以在调用 Update-Database 或任何其他 EF Core 工具时包含环境变量:

    $env:EFCORETOOLSDB = "Data Source=(local);Initial Catalog=ApplicationDb;Integrated Security=True"; Update-Database
    

    【讨论】:

    • 通过 PM 控制台设置(包含)环境变量见this answer
    【解决方案2】:

    我就是这样做的,没有太多额外的代码或疯狂。

    项目结构:

    • AspNetCoreProject.Web

    • AspNetCoreProject.Data

    我的 DbContext 设置了允许您注入 DbContextOptions 的构造函数

    AspNetCoreProject.Data

    public class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options) : base(options)
        {
        }
    }
    

    在您的应用程序或网络应用程序中,您通常设置您的ConfigureServices

    AspNetCoreProject.Web / Startup.cs / ConfigureServices()

    services.AddDbContext<MyContext>(options => 
                options.UseSqlServer(Configuration.GetConnectionString("connection"))
    

    现在,迁移呢?好吧,我“欺骗”了 Visual Studio UI 使其按预期工作。

    • 首先,确保您的应用程序(AspNetCoreProject.Web 项目与Startup.cs)是start up project

    • 其次,打开 Nuget 包管理器控制台。在 Nuget PM> 控制台的顶部,有一个 “设置默认项目” 的下拉菜单,将其指向您的 AspNetCoreProject.Data 或具有 DbContext 类的项目。

    • 正常运行migration commandsadd-migration init 然后update-database

    【讨论】:

    • 这是否意味着 dotnet ef 迁移将检查 Startup.cs 并找出那里的连接字符串?
    • 不准确,但我不够聪明,无法回答这个问题。但它确实有效。
    • 是的,确实如此。我通过简单地将Console.WriteLine($"&gt;&gt; CONNECTION STRING: '{connectionString}'."); 放入Startup 类的ConfigureServices(IServiceCollection services) 方法来检查它,这一行打印在PM 控制台 中。
    • 我不想在我的表示层中引用我的数据层 DBContext 类,所以我将它与另一个 Stack 答案结合起来,它允许我将 services.AddDbContext 卸载到我的业务层。 stackoverflow.com/questions/38356990/…
    • 只是不起作用。运行“添加迁移”时,连接字符串仍然为空
    【解决方案3】:

    我们遇到了同样的问题,并且有解决方案。 :)

    你必须实现IDbContextFactory<TContext> 这样做时,您可以从 appsettings.json 中读取连接字符串。您也可以使用 Add-Migration 而不会出错,因为此时覆盖 OnConfigure() 已过时。

    示例实现:

    public class DomainContextFactory : IDbContextFactory<DomainContext>
    {
        public string BasePath { get; protected set; }
    
        public DomainContext Create()
        {
            var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
    
            var basePath = AppContext.BaseDirectory;
    
            return Create(basePath, environmentName);
        }
    
        public DomainContext Create(DbContextFactoryOptions options)
            => Create(options.ContentRootPath, options.EnvironmentName);
    
        private DomainContext Create(string basePath, string environmentName)
        {
            BasePath = basePath;
            var configuration = Configuration(basePath, environmentName);
            var connectionString = ConnectionString(configuration.Build());
            return Create(connectionString);
        }
    
        private DomainContext Create(string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new ArgumentException($"{nameof(connectionString)} is null or empty", nameof(connectionString));
            }
            var optionsBuilder = new DbContextOptionsBuilder<DomainContext>();
            return Configure(connectionString, optionsBuilder);
        }
    
        protected virtual IConfigurationBuilder Configuration(string basePath, string environmentName)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(basePath)
                .AddJsonFile("constr.json")
                .AddJsonFile($"constr.{environmentName}.json", true)
                .AddEnvironmentVariables();
            return builder;
        }
    
        protected virtual string ConnectionString(IConfigurationRoot configuration)
        {
            string connectionString = configuration["ConnectionStrings:DefaultConnection"];
            return connectionString;
        }
    
        protected virtual DomainContext Configure(string connectionString, DbContextOptionsBuilder<DomainContext> builder)
        {
            builder.UseSqlServer(connectionString, opt => opt.UseRowNumberForPaging());
    
            DomainContext db = new DomainContext(builder.Options);
            return db;
        }
    
    
        DomainContext IDbContextFactory<DomainContext>.Create(DbContextFactoryOptions options)
            => Create(options.ContentRootPath, options.EnvironmentName);
    }
    

    我们如何使用它:

        public override IServiceResult<IList<Datei>> LoadAllData()
        {
            using (var db = this.DomainContextFactory.Create())
            {
                var files = db.Datei
                    .ToListAsync<Datei>();
    
                return new ServiceResult<IList<Datei>>(files.Result, files.Result.Count);
            }
        }
    

    示例配置

    {
      "ConnectionStrings": {
        "DefaultConnection": "Put your connectionstring here"
      }
    }
    

    【讨论】:

    • 只要您在 IoC 框架之前可以正常工作,它就可以正常工作。您基本上必须维护用于解析连接字符串的代码,以独立用于您的 IoC 和这些工厂。由于您无法将连接字符串提供程序或字符串本身之类的任何内容注入到您的 IDbContextFactory 中,因此您必须在工厂中嵌入连接字符串解析代码。无限复制此广告,您将面临维护噩梦。缺乏注入手段似乎是 EF 团队的重大疏忽。当然有更好的方法。
    • @crush 是的,没错。有一种新方法可以避免此代码。您只需要在 appsettings.json 中包含 Connectionstring,然后在专门的环境 *.json 文件中即可。在您的 ConfigureServices 中,您可以使用 options.UseSqlServer(this.configuration.GetconnectionString("NameFromJson"));
    【解决方案4】:

    我在下面使用 OnConfiguring 并在 Windows 环境变量 MsSql.ConnectionString 中进行了配置,并且用于初始 ef 迁移创建的命令开始工作:dotnet ef migrations add InitialCreate

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectionString = Environment.GetEnvironmentVariable("MsSql.ConnectionString");
        if(string.IsNullOrEmpty(connectionString))
            throw new ConfigurationErrorsException("Sql server connection string configuration required");
        
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder
                .UseSqlServer(connectionString)
                .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
        }
    }
    

    配置环境变量:

    1. 使用Win + R快捷键组合打开Run命令窗口
    2. 输入 systempropertiesadvanced 并回车
    3. Advanced 选项卡上单击Environment Variables
    4. 点击New...按钮
    5. Variable name字段类型MsSql.ConnectionString
    6. Variable value 字段中输入您的连接字符串值

    确保在添加新变量后和运行dotnet ef 相关命令之前重新启动控制台(以及任何启动控制台的程序)

    【讨论】:

      【解决方案5】:

      假设您的 DbContext 类有一个接受 DbContextOptions 类型参数的构造函数,dotnet ef 命令对这种情况具有本机支持 - 无需更改代码或额外配置。只需在创建和运行迁移时使用“--startup-project”和“--project”参数。

      例如,假设您有一个带有您的配置的“应用程序”项目和一个名为“域”的单独项目,其中实现了 DbContext。

      上下文:

      public class MyContext : DbContext
      {
          public MyContext(DbContextOptions<MyContext> options) : base(options)
          {
          }
      } 
      

      启动:

      services.AddDbContext<MyContext>(options => 
                  options.UseSqlServer(Configuration.GetConnectionString("connection"))
      

      CLI 命令:

      dotnet ef database update --startup-project Application --project Domain
      

      【讨论】:

        【解决方案6】:

        我的控制台应用程序中有我的 DBContext,并且正在使用带有少量参数(例如连接字符串等)的 ctor,因为 EF Core Migrations 使用的是默认参数 less ctor,因此我没有填充连接字符串迁移失败。

        刚刚添加了代码以从我的默认 ctor 中的 ConfigurationBuilder 获取连接字符串,以绕过这个。

        只是在玩控制台应用程序和 EF Core,所以现在这对我有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-06-15
          • 1970-01-01
          • 2017-10-27
          • 2022-10-25
          • 2020-01-07
          • 2018-11-05
          • 2020-03-09
          相关资源
          最近更新 更多