EnvoyConfig is a modern C# library for .NET 8+ that loads strongly-typed configuration objects from environment variables at runtime using reflection. It supports advanced attribute-based mapping, type safety, nested objects, lists, dictionaries, and flexible prefixingβall with minimal boilerplate.
- Attribute-based configuration:
[EnvoyConfig]attribute for mapping properties to env vars - Enhanced enum support with
[EnvoyConfigValue]attribute for custom string mappings - Supports primitives, enums, nullable types, lists, arrays, dictionaries, and nested objects
- Multiple initialization modes: direct key, comma-separated/numbered lists, dictionary/map, nested prefix
- Global/static prefix support
- Logging integration with adapters (Microsoft, Serilog, NLog)
- Thread-safe, high performance (caching)
- Zero external dependencies in core
β οΈ Deprecation Notice: The old[Env]and[EnvValue]attributes are deprecated and will be removed in the coming months. Please migrate to[EnvoyConfig]and[EnvoyConfigValue]respectively. The old attributes currently show compiler warnings to guide the migration.
See here for Documentation.
dotnet add package EnvoyConfigOptional:
dotnet add package EnvoyConfig.Adapters.Microsoft # For Microsoft.Extensions.Loggingpublic class ApiKeyConfig
{
[EnvoyConfig(Key = "PRIMARY")] // Maps to: MYAPP_API_PRIMARY
public string Primary { get; set; } = "";
[EnvoyConfig(Key = "SECONDARY")] // Maps to: MYAPP_API_SECONDARY
public string Secondary { get; set; } = "";
[EnvoyConfig(Key = "ENABLED", Default = true)] // Maps to: MYAPP_API_ENABLED
public bool Enabled { get; set; }
}
public class MyConfig
{
[EnvoyConfig(Key = "PORT", Default = 8080)] // Maps to: MYAPP_PORT
public int Port { get; set; }
[EnvoyConfig(Key = "FEATURES", IsList = true)] // Maps to: MYAPP_FEATURES (comma-separated)
public List<string> Features { get; set; } = new();
[EnvoyConfig(NestedPrefix = "API_")] // Maps to: MYAPP_API_* (nested object)
public ApiKeyConfig ApiKeys { get; set; } = new();
[EnvoyConfig(ListPrefix = "SERVER_")] // Maps to: MYAPP_SERVER_1, MYAPP_SERVER_2, etc.
public List<string> Servers { get; set; } = new();
}
// Set global prefix for all environment variables
EnvConfig.GlobalPrefix = "MYAPP_";
// Load configuration from environment variables
var config = EnvConfig.Load<MyConfig>();
// Save current configuration to .env file
EnvConfig.Save(config, "current-config.env");
// Save defaults template to .env file
EnvConfig.SaveDefaults<MyConfig>("template.env");Example Environment Variables:
MYAPP_PORT=3000
MYAPP_FEATURES=auth,logging,metrics
MYAPP_API_PRIMARY=key123abc
MYAPP_API_SECONDARY=key456def
MYAPP_API_ENABLED=true
MYAPP_SERVER_1=api.example.com
MYAPP_SERVER_2=backup.example.comEnvoyConfig provides enhanced enum support with the [EnvoyConfigValue] attribute, allowing you to map custom string values to enum members:
public enum TranscodingFormat
{
[EnvoyConfigValue("off", "disabled", "none")]
Disabled,
[EnvoyConfigValue("mp3")]
Mp3,
[EnvoyConfigValue("ogg", "vorbis")]
Ogg,
Flac // No attribute - uses standard enum parsing
}
public class AudioConfig
{
[EnvoyConfig(Key = "TRANSCODING_FORMAT", Default = TranscodingFormat.Disabled)]
public TranscodingFormat Format { get; set; }
}Environment Variables:
# Any of these values will map to TranscodingFormat.Disabled
MYAPP_TRANSCODING_FORMAT=off
MYAPP_TRANSCODING_FORMAT=disabled
MYAPP_TRANSCODING_FORMAT=none
# This maps to TranscodingFormat.Mp3
MYAPP_TRANSCODING_FORMAT=mp3
# Either of these map to TranscodingFormat.Ogg
MYAPP_TRANSCODING_FORMAT=ogg
MYAPP_TRANSCODING_FORMAT=vorbis
# Standard enum parsing still works
MYAPP_TRANSCODING_FORMAT=FlacWhen saving configurations, the first value from the [EnvoyConfigValue] attribute is used:
var config = new AudioConfig { Format = TranscodingFormat.Disabled };
EnvConfig.Save(config, "config.env");
// Outputs: MYAPP_TRANSCODING_FORMAT=off- Prefix Handling: Set
EnvConfig.GlobalPrefixto prepend to all lookups. - Attribute Modes:
[EnvoyConfig(Key = "FOO")](direct key)[EnvoyConfig(Key = "BAR", IsList = true)](comma-separated list)[EnvoyConfig(ListPrefix = "ITEM_")](numbered list: ITEM_1, ITEM_2, ...)[EnvoyConfig(MapPrefix = "MAP_")](dictionary: MAP_key1=val1, MAP_key2=val2)[EnvoyConfig(NestedPrefix = "DB_")](nested object)
- Supported Types: Primitives (string, int, bool, double, etc.),
DateTime,TimeSpan,Guid, enums, nullable types, List, T[], Dictionary<TKey,TValue>. Custom types can be supported viaITypeConverter. - Logging: Pass a custom logger (
IEnvLogSink) or use an adapter for your framework. - Custom Type Converters: Implement
ITypeConverterand register withTypeConverterRegistry.RegisterConverter()to handle custom string-to-type conversions. - Error Handling: Configure
EnvConfig.ThrowOnConversionError(defaultfalse) to control behavior when type conversion fails. Iftrue, exceptions are thrown; otherwise, errors are logged and default values are used.
EnvoyConfig allows you to define and register custom converters for types that are not natively supported or when you need specific parsing logic.
-
Implement
ITypeConverter: Create a class that implements theEnvoyConfig.Conversion.ITypeConverterinterface. The core method isobject? Convert(string? value, Type targetType, IEnvLogSink? logger).// Example: A simple Point class and its converter public class Point { public int X { get; set; } public int Y { get; set; } } public class PointConverter : ITypeConverter { public object? Convert(string? value, Type targetType, IEnvLogSink? logger) { if (string.IsNullOrWhiteSpace(value)) return null; var parts = value.Split(','); if (parts.Length == 2 && int.TryParse(parts[0], out int x) && int.TryParse(parts[1], out int y)) { return new Point { X = x, Y = y }; } logger?.Log(EnvLogLevel.Error, $"Cannot convert '{value}' to Point."); return null; } }
-
Register the Converter: Before loading your configuration, register an instance of your converter:
using EnvoyConfig.Conversion; // ... TypeConverterRegistry.RegisterConverter(typeof(Point), new PointConverter()); // ... // var config = EnvConfig.Load<YourConfigWithAPointProperty>();
Control how EnvoyConfig behaves when a type conversion from a string environment variable to a target property type fails:
EnvConfig.ThrowOnConversionError(static property):-
false(Default): If a conversion fails (e.g., "abc" cannot be parsed as anint), EnvoyConfig logs an error (if a logger is provided) and the property is set to its default C# value (e.g.,0forint,nullfor reference types). -
true: If a conversion fails, EnvoyConfig will throw anInvalidOperationExceptiondetailing the conversion issue. This allows for immediate and strict error handling.// Example: EnvConfig.ThrowOnConversionError = true; // Optional: for stricter error handling try { // var config = EnvConfig.Load<MyConfig>(); } catch (InvalidOperationException ex) { // Handle conversion errors }
-
- Type conversion errors: Check environment variable values and target property types. If
EnvConfig.ThrowOnConversionErrorisfalse(default), check logs for details and verify if the property has its default C# value. Iftrue, an exception will be thrown. - Missing env vars: Use
Defaultattribute property or handlenull/default values in your application logic. - Prefix confusion: Ensure
GlobalPrefixand attribute keys/prefixes are set as intended. - Enum parsing: Use
[EnvoyConfigValue]attribute for custom string mappings, or rely on standard case-insensitive enum name parsing. - Logging: Implement
IEnvLogSinkor use provided adapters for structured logs, especially to diagnose conversion issues whenThrowOnConversionErrorisfalse.
Contributions are welcome! Please open issues or PRs for bugs, features, or questions.
LGPL-3.0-or-later. See LICENSE.