Asp.net-mvc – How to dynamically choose a connection string based on Environment in .Net Core startup

asp.net-coreasp.net-mvc

In the StartUp class's Configure method of ASP.Net MVC Core, "IHostingEnvironment env" is passed in via Dependency Injection.
And decisions can be made based on the environment. For example,

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

In ConfigureServices, I want to do something like this to pick the correct Connection string.
Something like:

        if (env.IsDevelopment())
        {
            services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        }
        else if (env.IsStaging())
        {
            // Add Staging Connection
        }
        else
        {
            // Add Prod Connection
        }

But "IHostingEnvironment env" is not passed in to the ConfigureServices method by default.
So I modify the signature from:

public void ConfigureServices(IServiceCollection services)

to

public void ConfigureServices(IServiceCollection services, IHostingEnvironment env)

and in ConfigureServices put:

        if (env.IsDevelopment())
        {
            services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        }
        else if (env.IsStaging())
        {
            // Add Staging Connection
        }
        else
        {
            // Add Prod Connection
        }

So now when I run I get this error message:

"The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection."

ConfigureServices() won't take in the IHostingEnvironment var.

But "Startup.StartUp(IHostingEnvironment env)" does.
I thought about adding a StartUp class field and setting it to the correct environment from Startup() and then using that field to make the decision flow in ConfigureServices. But this seems like a hack.

I know environment is a first class concept in .Net Core.
Is there a way to do this straight from appsetting.json?

What is the best practice to accomplish this?

Best Answer

Update Like Sam said, Joe's solution, though works fine, this is a hack. As of ASP.Net Core 1.1, the correct approach of using different connection for different environment is to handle it from the appsettings.json file itself rather then checking for the environment in the ConfigureServices method. The project by default comes with appsettings.json and appsettings.Development.json files (a different one for Staging can be created manually using the naming convention, if need be)

So in Joe's solution you will not need to set the value of the environment in the constructor nor need to use it to check hosting environment it in the ConfigurServices. So get rid of:

environment = env;
........

public IHostingEnvironment environment { get; set; }
........
//Also, get rid of if /else in the ConfigureServices method.

So your ConfigureServices method will just inject:

services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Now, your appsettings.json will have:

  "ConnectionStrings": {
    "DefaultConnection": "//Value of your connection string for Production Env."
  }

and your appsettings.Development.json will have:

"ConnectionStrings": {
        "DefaultConnection": "//Value of your connection string for Development Env."
      }

Now, if you look at the constructor method, var builder already loads both appsettings files and depending upon if you are running the project in development environment or published production environment, it will use the correct file and hence the settings defined in them.

Related Topic