如何在.Net Core启动时根据环境动态选择连接字符串?
Posted
技术标签:
【中文标题】如何在.Net Core启动时根据环境动态选择连接字符串?【英文标题】:How do I dynamically choose a connection string based on Environment in .Net Core startup? 【发布时间】:2016-12-26 14:03:02 【问题描述】:在 ASP.Net MVC Core 的 StartUp 类的 Configure 方法中,“IHostingEnvironment env”是通过依赖注入传入的。 并且可以根据环境做出决策。例如,
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
else
app.UseExceptionHandler("/Home/Error");
在 ConfigureServices 中,我想做这样的事情来选择正确的连接字符串。 比如:
if (env.IsDevelopment())
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
else if (env.IsStaging())
// Add Staging Connection
else
// Add Prod Connection
但默认情况下“IHostingEnvironment env”并没有传递给 ConfigureServices 方法。 所以我从以下位置修改签名:
public void ConfigureServices(IServiceCollection services)
到
public void ConfigureServices(IServiceCollection services, IHostingEnvironment env)
并在 ConfigureServices 中放置:
if (env.IsDevelopment())
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
else if (env.IsStaging())
// Add Staging Connection
else
// Add Prod Connection
所以现在当我运行时,我会收到以下错误消息:
“ConfigureServices 方法必须要么是无参数的,要么只接受一个 IServiceCollection 类型的参数。”
ConfigureServices() 不会接受 IHostingEnvironment 变量。
但是“Startup.StartUp(IHostingEnvironment env)”可以。 我考虑过添加一个 StartUp 类字段并将其从 Startup() 设置为正确的环境,然后使用该字段在 ConfigureServices 中进行决策流程。但这似乎是一个 hack。
我知道环境是 .Net Core 中的一流概念。 有没有办法直接从 appsetting.json 做到这一点?
实现这一目标的最佳做法是什么?
【问题讨论】:
How to add DbContext based on environment in ASP.net Core的可能重复 【参考方案1】:更新 就像 Sam 所说,Joe 的解决方案虽然效果很好,但这是一个 hack。从 ASP.Net Core 1.1 开始,为不同环境使用不同连接的正确方法是从 appsettings.json 文件本身处理它,而不是在 ConfigureServices 方法中检查环境。该项目默认带有 appsettings.json 和 appsettings.Development.json 文件(如果需要,可以使用命名约定手动创建不同的 Staging 文件)
因此,在 Joe 的解决方案中,您无需在构造函数中设置环境的值,也无需在 ConfigurServices 中使用它来检查托管环境。所以摆脱:
环境 = 环境; ........
公共 IHostingEnvironment 环境 get;放; ........ //另外,去掉ConfigureServices方法中的if /else。
所以你的 ConfigureServices 方法只会注入:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
现在,您的 appsettings.json 将拥有:
"ConnectionStrings":
"DefaultConnection": "//Value of your connection string for Production Env."
您的 appsettings.Development.json 将拥有:
"ConnectionStrings":
"DefaultConnection": "//Value of your connection string for Development Env."
现在,如果您查看构造函数方法,var builder 已经加载了两个 appsettings 文件,并且取决于您是在开发环境还是已发布的生产环境中运行项目,它将使用正确的文件以及其中定义的设置。
【讨论】:
值得一提的是,永远不要将生产敏感数据存储在这样的配置中。我知道这只是一个例子,但.. 永远不知道人们如何滥用东西:) 对于产品配置,要走的路总是使用环境变量。【参考方案2】:IHostingEnvrionment 被传递到 Startup 的构造函数中,从那里你可以将它持久化到一个属性中,然后从 ConfigureSerivces 中使用它
public Startup(IHostingEnvironment env)
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.env.EnvironmentName.json", optional: true);
// add this file name to your .gitignore file
// so you can create it and use on your local dev machine
// remember last config source added wins if it has the same settings
builder.AddJsonFile("appsettings.dev.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
environment = env;
public IHostingEnvironment environment get; set;
public void ConfigureServices(IServiceCollection services)
if (envirnoment.IsDevelopment())
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
else if (envirnoment.IsStaging())
// Add Staging Connection
else
// Add Prod Connection
【讨论】:
这对我来说似乎不是最佳实践,或者 Microsoft ASP.Net Core 团队只会让您像在 Configure() 和 Startup() 中那样将 IHostingEnvironment 注入到 ConfigureServices() 中。这是我在原始帖子中已经想到但正在寻找更好方法的hack。我想我可能会继续这样做,直到找到正确的解决方案。不过感谢您的反馈。【参考方案3】:您也可以利用Environment based Startup class and methods 来解决这个问题。所以,你可以这样做:
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ProductionConnection")));
public void ConfigureStagingServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("StagingConnection")));
public void ConfigureDevelopmentServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
就环境之间的公共服务而言,您可以将它们提取到私有方法中,然后在 ConfigureEnvironmentServices 上调用它。此外,请考虑在源代码管理中没有生产连接字符串。您可以将其设置为Environment Variable on the machine.
【讨论】:
以上是关于如何在.Net Core启动时根据环境动态选择连接字符串?的主要内容,如果未能解决你的问题,请参考以下文章
根据 .Net Core 构建选择正确的 Angular 环境
ASP.Net Core:更改密码时数据库连接失败,但重新启动应用程序时可以工作
.Net Core 5 控制台应用程序,在 Program.Main 中完成时不会为环境加载应用程序设置