Managing environment variables efficiently is crucial for modern .NET applications, especially when configuring environments like development, staging, and production.
.NET Core's IConfiguration
interface is a powerful tool for managing application settings, and it can be extended to include environment variables stored in a .env
file using a custom configuration provider.
In This Series
- Reading .env Files in C#
- Loading DotEnv file into IConfiguration (You're here)
- Creating NuGet Package for EnvReader (coming soon)
In this post, we’ll demonstrate how to integrate the EnvReader
class into IConfiguration
, enabling seamless configuration management for your C# applications with support for DotEnv files.
This article is a follow-up to our previous post, Reading a .env File in C#, where we introduced the
EnvReader
class to load environment variables from a.env
file.
Setting Up the Project
Create a new .NET Standard Class Library and add the dependency packages.
# creates class library
dotnet new classlib -o Extensions.Configuration.EnvFile
# add to solution
dotnet sln add Extensions.Configuration.EnvFile/Extensions.Configuration.EnvFile.csproj
#add dependency packages
dotnet add package Microsoft.Extensions.Configuration.Abstractions
dotnet add package Microsoft.Extensions.Configuration.FileExtensions
The .csproj
file should look like this.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
</ItemGroup>
</Project>
Implementing the Configuration reader
To have a fully functional EnvReader
integrated with Microsoft.Extensions.Configuration.IConfiguration
interface lets us set up 4 different classes.
- EnvConfigurationExtensions - Here we will have the AddEnvFile method and overloads
- EnvConfigurationProvider - Provider to read the .env file
- EnvConfigurationSource - Source of Configuration that integrates with the Provider
- EnvReader - The .env file reader/parser
Step 1: Set Up the EnvReader
Class
For this version of EnvReader let's use Stream instead of file path.
using System.Collections.Generic;
using System.IO;
internal static class EnvReader
{
public static IEnumerable<KeyValuePair<string, string>> Load(Stream stream)
{
StreamReader reader = new StreamReader(stream);
while (reader.Peek() > -1)
{
string line = reader.ReadLine();
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
continue; // Skip empty lines and comments
var parts = line.Split('=', 2);
if (parts.Length != 2)
continue; // Skip lines that are not key-value pairs
var key = parts[0].Trim();
var value = parts[1].Trim();
yield return new KeyValuePair<string, string>(key, value);
}
}
}
Step 2: Create a Custom Configuration Provider
To integrate EnvReader
into IConfiguration
, let's create a custom configuration provider.
This provider will load environment variables from a .env
file and make them available through IConfiguration
.
using Microsoft.Extensions.Configuration;
using System.IO;
internal class EnvConfigurationProvider : FileConfigurationProvider
{
public EnvConfigurationProvider(FileConfigurationSource source) : base(source)
{
}
public override void Load(Stream stream)
{
foreach (var item in EnvReader.Load(stream))
{
Data[item.Key] = item.Value;
}
}
}
Step 3: Create a Custom Configuration Source
Next, let's create a custom configuration source that will be used to add our EnvConfigurationProvider
to the IConfigurationBuilder
.
using Microsoft.Extensions.Configuration;
public class EnvConfigurationSource : FileConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaults(builder);
return new EnvConfigurationProvider(this);
}
}
Step 4: Add Extension Method to IConfigurationBuilder
To make it easy to use our custom configuration provider, create a set of extension methods to register the EnvReader Provider.
To make registering easier, create extension methods in the namespace Microsoft.Extensions.Configuration
using Extensions.Configuration.EnvFile;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical;
using System;
namespace Microsoft.Extensions.Configuration;
public static class EnvConfigurationExtensions
{
public static IConfigurationBuilder AddEnvFile(
this IConfigurationBuilder builder,
string path = ".env",
bool optional = false,
bool reloadOnChange = true)
{
var fileProvider = new PhysicalFileProvider(AppContext.BaseDirectory, ExclusionFilters.Hidden | ExclusionFilters.System);
return AddEnvFile(builder, path: path, optional: optional, reloadOnChange: reloadOnChange, provider: fileProvider);
}
public static IConfigurationBuilder AddEnvFile(
this IConfigurationBuilder builder,
IFileProvider provider,
string path,
bool optional,
bool reloadOnChange)
{
if (builder == null)
throw new ArgumentNullException(nameof(builder));
if (string.IsNullOrEmpty(path))
throw new ArgumentException("invalid path", nameof(path));
return builder.AddEnvFile(s =>
{
s.FileProvider = provider;
s.Path = path;
s.Optional = optional;
s.ReloadOnChange = reloadOnChange;
s.ResolveFileProvider();
});
}
public static IConfigurationBuilder AddEnvFile(
this IConfigurationBuilder builder,
Action<EnvConfigurationSource> configureSource)
=> builder.Add(configureSource);
}
Demonstration
Now that everything is set up, let's integrate it into an ASP.NET Core application.
In the Program.cs
or Startup.cs
, add the following code to include your .env
file in the configuration.
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvFile();
var app = builder.Build();
app.MapGet("/", (IConfiguration configuration) =>
{
// Access the environment variables
string apiKey = configuration["API_KEY"] ?? throw new ArgumentException("Missing API_KEY env variable");
string databaseUrl = configuration["DATABASE_URL"] ?? throw new ArgumentException("Missing DATABASE_URL env variable");
string debug = configuration["DEBUG"] ?? throw new ArgumentException("Missing DEBUG env variable");
// Output the values
Console.WriteLine($"API Key: {apiKey}");
Console.WriteLine($"Database URL: {databaseUrl}");
Console.WriteLine($"Debug Mode: {debug}");
return new { apiKey, databaseUrl, debug };
});
app.Run();
Run the application and we should see the result below.
Source Code
Source code available on Github repo.
GitHub - ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles: https://github.com/ricardodemauro/Rmauro.Extensions.Configuration.EnvFiles
Conclusion
By extending IConfiguration
with a custom configuration provider, you can easily manage environment variables from a .env
file in your ASP.NET Core applications.
This approach keeps your configuration clean and organized, making it easier to handle different environments without hardcoding sensitive values.
Again, feel free to enhance this solution by adding features like default values, nested configuration, or more sophisticated parsing logic. Happy coding! 😎
Top comments (2)
This is over-engineered. Overcomplicated, bloated code. And not secure. Use smarter tools.
learn.microsoft.com/en-us/aspnet/c...
Hi Alex. How is this unsecure and over-engineered?