如果核心迁移不能使用秘密管理器

时间:2018-02-13 01:41:20

标签: visual-studio-2017 asp.net-core-2.0 entity-framework-core-migrations secret-manager

当我创建.net核心Web应用程序时,我在测试期间使用了秘密管理器。我通常能够创建一个新的Web项目(mvc和web api),右键单击该项目并选择“管理用户机密”。这将打开一个json文件,我在其中添加了秘密。然后我在我的startup.cs中使用它,如下所示:

services.AddDbContext<ApplicationDbContext>(options =>
    options.UseMySql(Configuration["connectionString"]));

该网站可以正常使用并与数据库连接良好。但是,当我尝试使用ef核心迁移命令(例如add-migration)时,它们似乎无法从秘密管理器访问连接字符串。我得到错误说“连接字符串不能为空”。当我用实际字符串硬编码Configuration["connectionString"]时,错误消失了。我已在线查看并检查.csproj文件,它们已包含以下行:

<UserSecretsId>My app name</UserSecretsId>

后来:

<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />

我需要添加任何内容,以便迁移可以访问连接字符串吗?

更新

我在上下文类中只有一个构造函数:

public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)
{
}

4 个答案:

答案 0 :(得分:3)

我目前也遇到了这个问题。我现在想出了一个工作的解决方案,但是最多可能会考虑凌乱。

我创建了一个配置类,在请求时提供配置接口:

public static class Configuration
{
    public static IConfiguration GetConfiguration()
    {
        return new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", true, true)
            .AddUserSecrets<Startup>()
            .AddEnvironmentVariables()
            .Build();
    }
}

在迁移中,您可以获取配置文件并访问其UserSecrets,如下所示:

protected override void Up(MigrationBuilder migrationBuilder)
{
    var conf = Configuration.GetConfiguration();
    var secret = conf["Secret"];
}

我已经测试了使用这些用户秘密创建一个SQL脚本,它可以工作(你显然不希望脚本存在,因为它会暴露实际的秘密)。

<强>更新

上述配置也可以在BuildWebHost方法中设置为Program.cs类:

 var config = new ConfigurationBuilder().AddUserSecrets<Startup>().Build();

 return WebHost.CreateDefaultBuilder(args).UseConfiguration(config)...Build()

如果使用that Convention

,则在Startup Constructor中

更新2(解释)

事实证明,此问题是因为迁移脚本在环境设置为“生产”的情况下运行。秘密经理预先设定为仅在“开发”环境中工作(有充分理由)。 .AddUserSecrets<Startup>()函数只是添加了所有环境的秘密。

为确保不将其设置为您的生产服务器,我注意到了两个解决方案,其中一个建议在此处:https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell

  

在运行之前设置env:ASPNETCORE_ENVIRONMENT以指定ASP.NET Core环境。

此解决方案意味着将来无需在计算机上创建的每个项目上设置.AddUserSecrets<Startup>()。但是,如果您碰巧在其他计算机上共享此项目,则需要在每台计算机上进行配置。

第二个解决方案是仅在调试版本上设置.AddUserSecrets<Startup>(),如下所示:

return new ConfigurationBuilder()
   .AddJsonFile("appsettings.json", true, true)
#if DEBUG
   .AddUserSecrets<Startup>()
#endif
   .AddEnvironmentVariables()
   .Build();    

其他信息

配置接口可以在其构造函数中传递给控制器​​,即

private readonly IConfiguration _configuration;
public TestController(IConfiguration configuration)
{
    _configuration = configuration;
}

因此,通过访问_configuration["secret"]可以在该控制器中访问任何秘密和应用程序设置。

但是,如果您想从例如Web应用程序本身之外的Migration-File访问Application Secrets,您需要遵循原始答案,因为没有简单的方法(我知道)否则访问这些秘密(我能想到的一个用例是用管理员和主密码为数据库播种)。

答案 1 :(得分:2)

要在NetCore中使用用户机密信息进行迁移,我们还可以设置一个类(SqlContextFactory),以使用指定的配置生成器创建自己的SqlContext实例。这样,我们不必在Program或Startup类中创建某种解决方法。在下面的示例中,SqlContextDbContext/IdentityDbContext的实现。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;

public class SqlContextFactory : IDesignTimeDbContextFactory<SqlContext>
{
    public SqlContext CreateDbContext(string[] args)
    {
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false)
            .AddUserSecrets<Startup>()
            .AddEnvironmentVariables()
            .Build();

        var builder = new DbContextOptionsBuilder<SqlContext>();
        builder.UseSqlServer(config.GetConnectionString("DefaultConnection"));
        return new SqlContext(builder.Options);
    }
}

答案 2 :(得分:2)

由于我已经注意到很多人陷入混乱,因此我正在编写此决议的简化版本。

问题/困惑

.net核心中的秘密管理器旨在仅在开发环境中工作。在运行应用程序时,您的launchSettings.json文件可确保将ASPNETCORE_ENVIRONMENT变量设置为“开发”。但是,当您运行EF迁移时,它不会使用此文件。结果,当您运行迁移时,您的Web应用程序不会在开发环境上运行,因此无法访问机密管理器。对于为什么EF迁移无法使用机密管理器,这常常引起困惑。

决议

确保在您的计算机中将环境变量“ ASPNETCORE_ENVIRONMENT”设置为“开发”。

答案 3 :(得分:0)

如果我们在单独的类库中使用DbContext并使用DesignTimeFactory,则使用.AddUserSecrets<Startup>()的方式将进行循环引用

干净的方法是:

            public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
        {
            public AppDbContext CreateDbContext(string[] args)
            {

                var configuration = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
#if DEBUG
                     .AddJsonFile(@Directory.GetCurrentDirectory() + "{project path}/appsettings.Development.json", optional: true, reloadOnChange: true)

 #else
                    .AddJsonFile(@Directory.GetCurrentDirectory() + "{startup project path}/appsettings.json", optional: true, reloadOnChange: true)
#endif
                    .AddEnvironmentVariables()
                    .Build();


                var connectionString = configuration.GetConnectionString("DefaultConnection");

                //Console.WriteLine(connectionString);

                var builder = new DbContextOptionsBuilder<AppDbContext>();


                Console.WriteLine(connectionString);
                builder.UseSqlServer(connectionString);
                return new AppDbContext(builder.Options);
            }
        }

说明:

Secret Manager仅在开发期间,因此,如果您在QA或Production阶段的管道中拥有它,这不会影响迁移,因此要修复此问题,我们将使用存在的dev连接字符串在appsettings.Development.json#if Debug中。

使用这种方法的好处是,在将类库用作数据基础结构时,可以取消对Web项目Startup类的引用。