如何在.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.jsonappsettings.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 环境

细说.net core Startup

ASP.Net Core:更改密码时数据库连接失败,但重新启动应用程序时可以工作

.Net Core 5 控制台应用程序,在 Program.Main 中完成时不会为环境加载应用程序设置

如何使用 .NET Core 根据发布配置文件更新 appsettings.json?

使用 Entityframework Core,如何在不更改连接字符串的情况下动态更改我连接的 MySql 数据库?