Managing Code for Different Environments
This document will go through how to maintain different versions of code for different environments. For example, having a test login endpoint on DEV and UAT, but disabling the endpoint for PROD. We will go through how to set things up so that there won’t be a need to modify code when building/deploying for different environments.
There are 2 different methods:
- Preprocessor Directives
- IHostEnvironment Extensions
Prerequisites
Before reading this document, make sure you understand the following:
- Build configurations
- Preprocessor directives
- Dotnet build command
- Dotnet publish command
- Dependency Injection
- IHostEnvironment
Sample Project
Refer to this sample project to see things in action.
Preprocessor Directives
Build Configurations
Firstly, create build configurations for every environment:
- DEV
- UAT/QA
- PROD (can use the default
Release
) - and any other environments as required
Assuming that you’ve added Dev
and UAT
build configurations, you should see the following in your csproj
file:
<Configurations>Debug;Release;UAT;Dev</Configurations>
Defining Preprocessor Directive Constants
Next, define the constants that you want to use for the different environments in the csproj
file.
For example:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>IS_PROD</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UAT|AnyCPU'">
<DefineConstants>IS_UAT</DefineConstants>
</PropertyGroup>
<!-- Set IS_DEV for local and DEV environments -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>IS_DEV</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Dev|AnyCPU'">
<DefineConstants>IS_DEV</DefineConstants>
</PropertyGroup>
Using Preprocessor Directives
After the setup, you can start using the preprocessor directives to write code that will be compiled for different platforms.
For example:
#if IS_PROD
RunProdLoginCode();
#elif IS_UAT
RunUatLoginCode();
#else
RunDevLoginCode();
#endif
Function implementations can also be wrapped in preprocessor directives to prevent the functions from being used outside of their intended environments.
Building and Publishing
For building and publishing, use the build or publish commands with the appropriate build configuration that is required.
For example to build for UAT:
dotnet build -c UAT
Once this has been set up correctly, CICD pipelines can also make use of this by using the appropriate build configurations in the commands.
IHostEnvironment
Using IHostEnvironment
IHostEnvironment extensions are available in .NET by default. Use dependency injection to add IHostEnvironment to the class as required.
Once done, you can use the extensions to manage environment-specific behaviours as required.
For example:
if (HostEnvironmentEnvExtensions.IsDevelopment(_hostEnvironment))
{
// run dev code
}
else {
// run non-dev code
}
Changing the host environment
We can change the host environment at runtime by adding the environment
flag when using dotnet run
.
For example to set the environment as Staging
:
dotnet run --environment Staging
Refer to the sample project for more information.