ef 核心迁移不能使用秘密管理器
ef core migration can't use secret manager
当我创建 .net 核心 Web 应用程序时,我在测试期间使用了秘密管理器。我通常能够创建一个新的 web 项目(mvc 和 web api),右键单击该项目并 select "manage user secrets"。这将打开一个 json 文件,我在其中添加了秘密。然后我在我的 startup.cs 中使用它,如下所示:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration["connectionString"]));
该网站在这方面运行良好,并且可以很好地连接到数据库。但是,当我尝试使用 add-migration
等 ef 核心迁移命令时,它们似乎无法从秘密管理器访问连接字符串。我收到错误提示 "connection string can't be null"。当我用实际字符串硬编码 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" />
我需要添加什么以便迁移可以访问连接字符串吗?
更新
上下文中我只有一个构造函数class:
public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)
{
}
我目前也遇到了这个问题。我想出了一个目前 有效 的解决方案,但人们可能认为充其量是混乱的。
我创建了一个配置 Class,它在请求时提供配置界面:
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.csclass:
var config = new ConfigurationBuilder().AddUserSecrets<Startup>().Build();
return WebHost.CreateDefaultBuilder(args).UseConfiguration(config)...Build()
或者在启动构造函数中使用 that Convention
更新2(解释)
原来这个问题是因为迁移脚本在环境设置为“生产”的情况下运行。秘密管理器 pre-set 只能在“开发”环境中工作(有充分的理由)。 .AddUserSecrets<Startup>()
函数只是为所有环境添加秘密。
为确保这不会设置到您的生产服务器,我注意到有两种解决方案,这里推荐一种:https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell
Set env:ASPNETCORE_ENVIRONMENT before running to specify the ASP.NET Core environment.
此解决方案意味着以后无需在计算机上创建的每个项目上设置 .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 访问应用程序机密,则需要遵循原始答案,因为没有简单的方法(即我知道)以其他方式访问这些秘密(我能想到的一个用例是使用管理员密码和主密码为数据库播种)。
要在 NetCore 中使用用户机密进行迁移,我们还可以设置 class (SqlContextFactory) 以使用指定的配置生成器创建自己的 SqlContext 实例。这样我们就不必在我们的程序或启动 classes 中创建某种解决方法。在下面的例子中 SqlContext
是 DbContext/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);
}
}
如果我们将 DbContext 放在单独的 class 库中并使用 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");
var builder = new DbContextOptionsBuilder<AppDbContext>();
Console.WriteLine(connectionString);
builder.UseSqlServer(connectionString);
return new AppDbContext(builder.Options);
}
}
解释:
Secret Manager 仅用于开发阶段,因此如果您在 QA 或生产阶段的管道中使用它,这不会影响迁移,因此要解决这个问题,我们将使用 [=] 中存在的开发连接字符串12=] 在 #if Debug
.
期间
使用这种方式的好处是在使用 class 库作为您的数据基础设施时,分离引用 Web 项目启动 class。
由于我注意到很多人 运行 对此感到困惑,因此我正在编写此解决方案的简化版本。
Problem/Confusion
.net core 中的秘密管理器旨在仅在开发环境 中工作。当 运行 连接您的应用程序时,您的 launchSettings.json 文件确保您的 ASPNETCORE_ENVIRONMENT
变量设置为 "Development"。但是,当您 运行 EF 迁移时,它不使用此文件。因此,当您 运行 迁移时,您的网络应用程序不会 运行 在开发环境中,因此无法访问秘密管理器。这通常会导致混淆,为什么 EF 迁移不能使用秘密管理器。
决议
确保您的计算机中的环境变量 "ASPNETCORE_ENVIRONMENT" 设置为 "Development"。
当我创建 .net 核心 Web 应用程序时,我在测试期间使用了秘密管理器。我通常能够创建一个新的 web 项目(mvc 和 web api),右键单击该项目并 select "manage user secrets"。这将打开一个 json 文件,我在其中添加了秘密。然后我在我的 startup.cs 中使用它,如下所示:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration["connectionString"]));
该网站在这方面运行良好,并且可以很好地连接到数据库。但是,当我尝试使用 add-migration
等 ef 核心迁移命令时,它们似乎无法从秘密管理器访问连接字符串。我收到错误提示 "connection string can't be null"。当我用实际字符串硬编码 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" />
我需要添加什么以便迁移可以访问连接字符串吗?
更新
上下文中我只有一个构造函数class:
public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options)
{
}
我目前也遇到了这个问题。我想出了一个目前 有效 的解决方案,但人们可能认为充其量是混乱的。
我创建了一个配置 Class,它在请求时提供配置界面:
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.csclass:
var config = new ConfigurationBuilder().AddUserSecrets<Startup>().Build();
return WebHost.CreateDefaultBuilder(args).UseConfiguration(config)...Build()
或者在启动构造函数中使用 that Convention
更新2(解释)
原来这个问题是因为迁移脚本在环境设置为“生产”的情况下运行。秘密管理器 pre-set 只能在“开发”环境中工作(有充分的理由)。 .AddUserSecrets<Startup>()
函数只是为所有环境添加秘密。
为确保这不会设置到您的生产服务器,我注意到有两种解决方案,这里推荐一种:https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell
Set env:ASPNETCORE_ENVIRONMENT before running to specify the ASP.NET Core environment.
此解决方案意味着以后无需在计算机上创建的每个项目上设置 .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 访问应用程序机密,则需要遵循原始答案,因为没有简单的方法(即我知道)以其他方式访问这些秘密(我能想到的一个用例是使用管理员密码和主密码为数据库播种)。
要在 NetCore 中使用用户机密进行迁移,我们还可以设置 class (SqlContextFactory) 以使用指定的配置生成器创建自己的 SqlContext 实例。这样我们就不必在我们的程序或启动 classes 中创建某种解决方法。在下面的例子中 SqlContext
是 DbContext/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);
}
}
如果我们将 DbContext 放在单独的 class 库中并使用 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");
var builder = new DbContextOptionsBuilder<AppDbContext>();
Console.WriteLine(connectionString);
builder.UseSqlServer(connectionString);
return new AppDbContext(builder.Options);
}
}
解释:
Secret Manager 仅用于开发阶段,因此如果您在 QA 或生产阶段的管道中使用它,这不会影响迁移,因此要解决这个问题,我们将使用 [=] 中存在的开发连接字符串12=] 在 #if Debug
.
使用这种方式的好处是在使用 class 库作为您的数据基础设施时,分离引用 Web 项目启动 class。
由于我注意到很多人 运行 对此感到困惑,因此我正在编写此解决方案的简化版本。
Problem/Confusion
.net core 中的秘密管理器旨在仅在开发环境 中工作。当 运行 连接您的应用程序时,您的 launchSettings.json 文件确保您的 ASPNETCORE_ENVIRONMENT
变量设置为 "Development"。但是,当您 运行 EF 迁移时,它不使用此文件。因此,当您 运行 迁移时,您的网络应用程序不会 运行 在开发环境中,因此无法访问秘密管理器。这通常会导致混淆,为什么 EF 迁移不能使用秘密管理器。
决议
确保您的计算机中的环境变量 "ASPNETCORE_ENVIRONMENT" 设置为 "Development"。