Think with Enlab

Diving deep into the ocean of technology

Stay Connected. No spam!
  • Home
  • Development
  • How to Configure .Net Core Environments with Practical Examples

How to Configure .Net Core Environments with Practical Examples

In this blog post, we will share our experiences at Enlab Software about managing the configuration of environments in .net core /asp.net core applications.

 

Table of Contents

 

 

What Is “Environment” in Software Development?

An environment in software development is the collection of hardware, software, and tools needed for building and running the software. Commonly, a popular setup can include development, staging, and production environments.

Using multiple environments ensures that your software is properly tested before deployment and release to users. Therefore, you should remember to always keep your app creation flow organized to maximize the benefit of the Environment.

 

How Environments Affect the Coding Process

The Environments manage the setting for each environment (app configurations, 3rd party packages, service configurations, or a feature that needs to be enabled/disabled on each environment...etc). Without a thorough configuration, the application may not function well in each environment and cause developers to rewrite codes.


Therefore, it is crucial to check and update your environment configuration correspondingly before you deploy it to the environment. Fortunately, .net core does support detecting the environment and loading the correct configuration by codes.

 

Configure .net Core Environments

How .net Core Support Environment Configurations

.net core uses environment variables to indicate in which environment the application is running and to allow the app to be configured appropriately. They provide a static class Environment in the system namespace to access the environment variables.
Example: I used GetEnvironmentVariable method to get the value of “windir” environment variable as below:

using System;

 

namespace ConsoleApp

{

    class Program

    {

        static void Main(string[] args)

        {

            var environment = Environment.GetEnvironmentVariable("windir");

 

            Console.WriteLine("windir path: " + environment);

 

            Console.ReadKey();

        }

    }

}


The result:


.net core config example 2.1 1

 

You will see the value of the “windir” environment variable via “C:\WINDOWS”.


.net core config example 2.1 2

 

When using Visual Studio, the environment variable can be specified during the development process in your project’s debug profiles. Below is my example:

 

.net core config example 2.1 3


How about configuration?

Configuration in .net core is performed using one or more configuration providers, which will read configuration data from key-value pairs using a variety of configuration sources, such as:

  • File configuration (INI, JSON, and XML files)
  • Command-line arguments
  • Environment variables
  • Key per file
  • Directory files
  • In-memory

 

Built-In Environments in .net Core/Asp.net Core

Example: In the asp.net core application, the “ASPNETCORE_ENVIRONMENT” variable and file configuration provider (appsettings.json file) is used by default.
When checking the asp.net core project template, you should see that the “ASPNETCORE_ENVIRONMENT” variable with the value “Development” is set by default.

 

.net core config example 2.2 1


Those are three values that are used by convention: Development, Staging, and Production.

Furthermore, we have two default files with appsettings files (appsettings.json and appsettings.Development.json).


.net core config example 2.2 2

Besides, in .net core, you can use the IHostingEnvironment service (Changed to IWebHostEnvironment from .net core 3) to work with environments and the IConfiguration to work with the appsettings files. Those services are already provided by the asp.net hosting layer and can be injected via Dependency Injection. You can see in the following asp.net project template:

.net core config example 2.2 3

See source code below:

public class Startup

    {

        public IConfiguration Configuration { get; }

        

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

        }

        public void ConfigureServices(IServiceCollection services)

        {

            services.AddControllers();

        }

 

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

        {

            if (env.IsDevelopment())

            {

                app.UseDeveloperExceptionPage();

            }

 

            app.UseHttpsRedirection();

 

            app.UseRouting();

 

            app.UseAuthorization();

 

            app.UseEndpoints(endpoints =>

            {

                endpoints.MapControllers();

            });

        }

    }


To get the configuration in appsettings files you can use IConfiguration service instance as follows:

  • AllowedHosts configuration in appsettings.json and appsettings.Development.json files

{

  "AllowedHosts": "*"

}

and

{

  "AllowedHosts": "http://localhost:5000"

}

 

  • Access via square brackets or GetValue method

.net core config example 2.2 4

See source code below (for the red highlighted part):

public class Startup

    {

        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

 

            var allowedHosts = configuration["AllowedHosts"];

 

            var allowedHosts1 = configuration.GetValue<string>("AllowedHosts");

        }

 

As can be seen from above, the AllowedHosts config appears in both appsettings.json and appsettings.Development.json files. However, the value in runtime is in appsettings.Development.json because the app is running in the “Development” environment. Subsequently, asp.net core will replace the value in the appsettings.json with the value in appsettings.{Environment}.json (with the Environment being the value of “ASPNETCORE_ENVIRONMENT” environment variable)

By changing the “ASPNETCORE_ENVIRONMENT” variable value, you can change the configuration value to the appsettings file accordingly.

  • If you are using Visual Studio:

.net core config example 2.2 6

You can also run this PowerShell command:
$Env:ASPNETCORE_ENVIRONMENT = "Your environment name"

It is possible to add more appsettings.{Environment}.json for your Environment if needed. See my implementation for “Staging” environment as follows:


.net core config example 2.2 5


Best Practice to Store and Load Environment Settings

Several Ways to Manage the Environment Settings in .net Core (with Code Example)

Let's try something advanced! Suppose that your application needs to send emails using SMTP and there are different configurations for each environment. What you can do is to store the SMTP configuration in appsettings files as below:

appsettings.Staging.json file:

{

"ConnectionString": "Server=192.168.2.231;Integrated Security=True;Database=Staging_DB;",

  "EmailSettings": {

    "SMTPLogin": "staging-email@gmail.com",

    "SMTPPassWord": "my-password",

    "SMTPPort": "587",

    "SMTPHostname": "smtp.gmail.com"

  }

}

 

appsettings.Development.json file

{

  "ConnectionString": "Server=localhost;Integrated Security=True;Database=Dev_DB;",

  "EmailSettings": {

    "SMTPEmail": "dev-email@gmail.com",

    "SMTPPassWord": "my-password",

    "SMTPPort": "587",

    "SMTPHostname": "smtp.gmail.com"

  }

}

 


Now, how can we retrieve the configuration value at runtime?

As I mentioned above, you can use IConfiguration service since it allows a way to retrieve configuration value, such as:

  • Get It Right Away

Basically, the appsettings.json file accepts any configuration in a JSON format and supports nested objects (such as EmailSettings above). To get the value directly, you can use the path that is separated by a colon (:):

public class WeatherForecastController : ControllerBase

    {

        private static readonly string[] Summaries = new[]

        {

            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"

        };

 

        public WeatherForecastController(IConfiguration configuration)

        {

            var smtpEmail = configuration.GetValue<string>("EmailSettings:SMTPEmail");

        }

 

  • Use IConfigurationSection with GetSection Method, Followed by GetValue

public class WeatherForecastController : ControllerBase

    {

        private static readonly string[] Summaries = new[]

        {

            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"

        };

 

        public WeatherForecastController(IConfiguration configuration)

        {

            var emailSettingsSection = configuration.GetSection("EmailSettings");

 

            var smtpEmail = emailSettingsSection.GetValue<string>("SMTPEmail");

        }

GetSection will return an IConfigurationSection instance that represents a section of application configuration values (in this case, it’s JSON object in appsettings file). As can be seen from above, it has returned the “EmailSettings” configuration.

  • Use Defined Class

Microsoft team also introduced the Options pattern, which allows us to have strong typed options. Once configured, they can inject the options into your services. Here is how you can use it:

  • Step 1: define a strong typed class to hold your configuration (EmailSettings in this case)

namespace WebApplication.Models

{

    public class EmailSettings

    {

        public string SMTPEmail { get; set; }

        public string SMTPPassword { get; set; }

        public int SMTPPort { get; set; }

        public string SMTPHostname { get; set; }

    }

}

 

  • Step 2: register it with IServiceCollection

public class Startup

    {

        public IConfiguration Configuration { get; }

        

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

        }

 

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            services.AddControllers();

 

            services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

        }

 

  • Final step: inject the options into your services, using the IOptions<T> interface with T being your defined class:

using Microsoft.AspNetCore.Mvc;

using System;

using System.Collections.Generic;

using System.Linq;

using WebApplication.Models;

using Microsoft.Extensions.Options;

 

 

namespace WebApplication.Controllers.old1

{

    [ApiController]

    [Route("[controller]")]

    public class WeatherForecastController : ControllerBase

    {

        public WeatherForecastController(IOptions<EmailSettings> options)

        {

            var emailSettings = options.Value;

        }

 

Below is my result after debugging:

.net core config example 3.1 1

 

Our Best Configuration Tips

From the above examples, using a defined class stands out as the most effective to us. Besides, it is highly recommended in MS documentation. Reflecting on my experience working with .net core, I would like to share some configuration tips and trips that can be useful for you:

  • Isolate your configuration by separating classes: the scenarios (classes) that depend on configuration settings are only affected by the configuration settings they use.
  • Make your defined class setters private: As the configurations are the read-only values and do not change during the application runtime, the private setters make sure that the configuration values can't be changed for any reason.
  • Use static properties for the configurations that you know will remain unchanged, otherwise you will need to reset the app.
    • Static properties with private setters

namespace WebApplication.Models

{

    public class EmailSettings

    {

        public static string SMTPEmail { get; private set; }

        public static string SMTPPassword { get; private set; }

        public static int SMTPPort { get; private set; }

        public static string SMTPHostname { get; private set; }

    }

}

 

  • After that, use the GET method with the option “BindNonPublicProperties = true” to set the values and you’’ be able to access your configuration values everywhere.

public void ConfigureServices(IServiceCollection services)

        {

            Configuration.GetSection("EmailSettings").Get<EmailSettings>(options => options.BindNonPublicProperties = true);

 

            var smtpEmail = EmailSettings.SMTPEmail;

 

            services.AddControllers();

        }

 

  • Register your configuration class instead of using Options pattern: Options pattern is a good choice when your configuration is unstable and should be recomputed on every request, or you want to manage the change options for notifications. However, it is less useful in my opinion because you won’t need it in most cases. If you must use it, please add the comment below:
    • Revert the static properties on the EmailSettings class but keep the private setters.

namespace WebApplication.Models

{

    public class EmailSettings

    {

        public string SMTPEmail { get; private set; }

        public string SMTPPassword { get; private set; }

        public int SMTPPort { get; private set; }

        public string SMTPHostname { get; private set; }

    }

}

 

  • Register EmailSettings instead of IOptions<EmailSetting>

 public class Startup

    {

        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

        }

 

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            var emailConfigSection = Configuration.GetSection("EmailSettings");

 

            services.AddControllers();

 

            services.AddSingleton<EmailSettings>(

                emailConfigSection.Get<EmailSettings>(options => options.BindNonPublicProperties = true)

            );

 

  • If you want to apply changes without restarting the app, you need to change the registration code to use AddScoped instead of AddSingleton:

public class Startup

    {

        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

        }

 

        // This method gets called by the runtime. Use this method to add services to the container.

        public void ConfigureServices(IServiceCollection services)

        {

            var emailConfigSection = Configuration.GetSection("EmailSettings");

 

            services.AddControllers();

 

            // services.AddSingleton<EmailSettings>(

            //     emailConfigSection.Get<EmailSettings>(options => options.BindNonPublicProperties = true)

            // );

 

            // Register for EmailSettings class

            services.AddScoped<EmailSettings>(sp =>

            {

                return emailConfigSection.Get<EmailSettings>(options => options.BindNonPublicProperties = true);

            });

        }

 

Conclusion

We hope our sharing can help you explore better the .Net core Environment configuration as it has many perks that are yet to be discovered. In our next blog about this topic, we will analyze “how to secure sensitive data in configuration” and provide you with hands-on examples. Stay tuned!

 

.net core config environment contact button

 


References


The Author

software engineer enlab software Tan Nguyen Hi, my name is Tan! I am currently working as a Software Engineer at Enlab Software and I mostly focus on Software Architectures, Development, and Software Re-factoring.
I love coding and learning new technologies so I'm excited to share my knowledge with you!

Up Next

How to Secure Sensitive Data in The Configuration
October 13,2020 by Tan Nguyen
Introduction On the blog post “How to Configure .Net Core Environments With Practical Examples”, we shared...
How IIS Processes ASP.NET Core HTTP Request
September 30,2020 by Vinh Tran
Have you ever wondered what happens under the hood when you make an API call to...
Effective techniques for software engineer
September 25,2020 by Vinh Tran
Introduction Software development methodologies were introduced and practiced in every project using Waterfall which is the...
top-down-approaching-in-programming
September 11,2020 by Vinh Tran
Overview Writing code becomes much easier when requirements are broken down into actionable items with given...