15 questions found
How would you write a custom configuration provider to load settings from an unsupported source, like a remote database?
Advanced
You implement IConfigurationSource (returning an IConfigurationProvider instance) and a corresponding ConfigurationProvider subclass overriding Load() to populate the provider's internal Data dictionary from your custom source, then register it via an extension method calling configurationBuilder.Add(new MyCustomSource()) -- this integrates seamlessly with the rest of the configuration system, including layering with other providers and (if you implement change detection) triggering hot-reload.
public class DatabaseConfigurationProvider : ConfigurationProvider {
public override void Load() {
using var connection = new SqlConnection(_connectionString);
var settings = connection.Query("SELECT [Key], [Value] FROM AppSettings");
foreach (var s in settings) Data[s.Key] = s.Value;
}
}
public class DatabaseConfigurationSource : IConfigurationSource {
public IConfigurationProvider Build(IConfigurationBuilder builder) => new DatabaseConfigurationProvider();
}
// Usage: builder.Configuration.Add(new DatabaseConfigurationSource());
Real-world example
A multi-tenant SaaS platform builds a custom configuration provider that loads tenant-specific feature flags from a database table at startup, integrating seamlessly alongside the standard appsettings.json and environment variable providers.
Common follow-ups: How would you add live change detection (polling or push notifications) to this custom provider?;How does provider registration order affect precedence for a custom source?
Entity Framework Core & Data Access;Multiple Inheritance & MRO
How does the command-line configuration provider let you override settings via arguments when running an application?
Intermediate
The command-line provider (added by default in the host builder) parses arguments in --Key=Value or --Key Value format (and supports colon-delimited nested keys) as the highest-precedence configuration source by default, letting you override any setting for a single run without modifying files or environment variables -- useful for quick local testing or scripted one-off overrides.
dotnet run --ConnectionStrings:DefaultConnection="Server=test;" --Logging:LogLevel:Default=Debug
// Equivalent to setting these keys via any other provider, but only for this single run
Real-world example
A developer debugging a production-like issue locally runs `dotnet run --Logging:LogLevel:Default=Trace` for one session to get maximally verbose logging without permanently changing any configuration file.
Common follow-ups: Does the command-line provider support short-form argument aliases?;How does this interact with environment-variable overrides in terms of precedence?
.NET CLI
SDK & Project Structure (csproj);Logging
How would you design configuration for a multi-tenant application where each tenant needs different settings resolved at runtime?
Advanced
Common approaches include: a custom configuration provider that loads and merges tenant-specific overrides on top of global defaults at request time (using the current tenant context, often resolved via IHttpContextAccessor or a middleware-set value), or a tenant-aware options resolution pattern (a custom ITenantOptionsProvider service) that layers a base IOptions<T> with tenant-specific overrides fetched from a database or cache -- since the standard Options pattern's IOptionsSnapshot is scoped per-request but not inherently tenant-aware, additional logic is needed to select the right values for the current tenant.
public class TenantSettingsResolver {
private readonly IOptionsMonitor<Dictionary<string, TenantSettings>> _allTenantSettings;
public TenantSettings GetForCurrentTenant(string tenantId) {
return _allTenantSettings.CurrentValue.TryGetValue(tenantId, out var settings)
? settings
: _defaultSettings;
}
}
Real-world example
A B2B SaaS platform resolves each tenant's custom branding colors, feature flags, and rate limits through a tenant-aware settings resolver that layers tenant-specific database overrides on top of a global default configuration baseline.
Common follow-ups: How do you cache tenant-specific settings efficiently without a database hit on every request?;How does this pattern interact with configuration hot-reload for tenant-specific changes?
Microservices & Distributed Architecture Patterns;Caching (In-Memory
Distributed & Redis)
What does GetSection and GetValue do differently when reading configuration values in ASP.NET Core?
Beginner
GetSection(key) returns an IConfigurationSection representing a subtree of configuration (useful for binding to an options class or enumerating children), returning an empty, non-null section even if the key doesn't exist (requires checking .Exists()). GetValue<T>(key) directly reads and converts a single leaf value to the specified type, returning the type's default (or a specified fallback) if the key is missing, making it more convenient for simple scalar reads.
var section = configuration.GetSection("Smtp"); // returns a section, even if "Smtp" doesn't exist
if (!section.Exists()) Console.WriteLine("Smtp section missing");
var port = configuration.GetValue<int>("Smtp:Port", defaultValue: 587); // direct scalar read with fallback
Real-world example
A configuration validation utility uses GetSection(...).Exists() to check for optional feature configuration blocks before attempting to bind them, avoiding null reference exceptions on genuinely optional settings.
Common follow-ups: What does IConfigurationSection.Value return for a section with children instead of a leaf value?;How does GetValue handle type conversion failures?
Configuration & Options;Diagnostics & Performance
How does the order of configuration provider registration affect which value wins when the same key is defined in multiple sources?
Intermediate
Configuration providers are evaluated in the order they're added to the ConfigurationBuilder, with each subsequently added provider's values overriding any previously set value for the same key -- meaning provider registration order directly determines precedence, which is why understanding and controlling this order (files first, then environment variables, then command-line args, as the conventional pattern) is essential for predictable configuration behavior across environments.
var builder = WebApplication.CreateBuilder(args);
// Default order already applied: appsettings.json -> appsettings.{Env}.json -> user secrets -> env vars -> command line
// Adding a custom source LAST gives it HIGHEST precedence
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string> {
["Override:Key"] = "this wins over everything registered before it"
});
Real-world example
A debugging session reveals a setting isn't taking effect because a custom configuration source was registered before the environment variable provider instead of after, causing the environment variable to silently override the intended custom value.
Common follow-ups: How would you inspect the final resolved value along with which provider supplied it?;Why does WebApplication.CreateBuilder set up this particular default order?
Configuration & Options;Secrets Management & Configuration Providers (Key Vault
User Secrets)