当我创建.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)
{
}
答案 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类中创建某种解决方法。在下面的示例中,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);
}
}
答案 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类的引用。