Skip to content

Using appsettings.json in ASP.NET Core 6+ Program.cs

Problem Statement

With the introduction of ASP.NET Core 6, Microsoft merged the Startup.cs and Program.cs files into a single streamlined Program.cs file using top-level statements. This architectural change left many developers unsure how to properly access configuration settings from appsettings.json and use dependency injection in the new minimal hosting model.

The core challenge is accessing configuration values and binding them to services during application startup without the traditional Startup class structure.

Solution Overview

ASP.NET Core 6+ provides several approaches to access configuration values from appsettings.json in the Program.cs file:

  1. Direct access using the built-in Configuration property
  2. Section binding to strongly-typed classes
  3. Dependency injection configuration for services

Accessing Configuration Directly

The simplest approach uses the Configuration property available on the WebApplicationBuilder:

csharp
var builder = WebApplication.CreateBuilder(args);

// Access individual values using colon-separated keys
var redisConfig = builder.Configuration["RedisCacheOptions:Configuration"];
var jwtKey = builder.Configuration["Jwt:Key"];

// Or use GetValue for type conversion
var testValue = builder.Configuration.GetValue<string>("TestValue");
var allowedOrigins = builder.Configuration.GetValue<string>("AllowedOrigins");

// For connection strings specifically
var connectionString = builder.Configuration.GetConnectionString("Default");

Binding Configuration Sections to Classes

For better maintainability and type safety, bind configuration sections to strongly-typed classes:

1. Define Configuration Classes

csharp
public class RedisCacheOptions
{
    public string Configuration { get; set; }
}

public class JwtSettings
{
    public string Key { get; set; }
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public TokenLifetime TokenLifetime { get; set; }
}

public class TokenLifetime
{
    public TimeSpan Mobile { get; set; }
    public TimeSpan Web { get; set; }
}

2. Bind and Use Configuration in Program.cs

csharp
var builder = WebApplication.CreateBuilder(args);

// Method 1: Manual binding
var redisOptions = new RedisCacheOptions();
builder.Configuration.GetSection("RedisCacheOptions").Bind(redisOptions);

// Method 2: Automatic binding with Get<T>()
var jwtSettings = builder.Configuration.GetSection("Jwt").Get<JwtSettings>();

// Use the configured values
builder.Services.AddStackExchangeRedisCache(options => 
{
    options.Configuration = redisOptions.Configuration;
});

Configuring Dependency Injection

For services that need configuration, use the Options pattern:

1. Register Configuration with DI Container

csharp
var builder = WebApplication.CreateBuilder(args);

// Register configuration sections for dependency injection
builder.Services.Configure<RedisCacheOptions>(
    builder.Configuration.GetSection("RedisCacheOptions"));
    
builder.Services.Configure<JwtSettings>(
    builder.Configuration.GetSection("Jwt"));

2. Inject Configuration in Services

csharp
// Service that uses configured options
public class MyService
{
    private readonly JwtSettings _jwtSettings;
    
    public MyService(IOptions<JwtSettings> jwtOptions)
    {
        _jwtSettings = jwtOptions.Value;
    }
    
    public string GetJwtKey() => _jwtSettings.Key;
}

Complete Practical Example

Here's a comprehensive example showing how to replace hardcoded values with configuration from appsettings.json:

appsettings.json

json
{
  "Redis": "localhost:6379",
  "Jwt": {
    "Key": "YourSecretKeyHere",
    "Issuer": "yourdomain.com",
    "Audience": "youraudience"
  },
  "AllowedOrigins": "https://localhost:3000;https://localhost:3001",
  "ConnectionStrings": {
    "Default": "Server=localhost;Database=MyDb;Trusted_Connection=true"
  }
}

Program.cs Implementation

csharp
var builder = WebApplication.CreateBuilder(args);

// Access configuration values
var redisConnection = builder.Configuration["Redis"];
var allowedOrigins = builder.Configuration.GetValue<string>("AllowedOrigins");

// Configure services with retrieved values
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = redisConnection;
});

// Configure CORS using settings from appsettings.json
if (!string.IsNullOrEmpty(allowedOrigins))
{
    builder.Services.AddCors(options =>
    {
        var origins = allowedOrigins.Split(";");
        options.AddPolicy("CorsPolicy", policy =>
        {
            policy.AllowAnyMethod()
                  .AllowAnyHeader()
                  .AllowCredentials()
                  .WithOrigins(origins);
        });
    });
}

// Register strongly-typed configuration
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("Jwt"));

var app = builder.Build();

// Use CORS middleware
app.UseCors("CorsPolicy");

// Remaining middleware configuration
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

Best Practices and Considerations

TIP

  • Use strongly-typed configurations whenever possible for better maintainability and type safety
  • Leverage the Options pattern for services that need configuration values
  • Validate configuration values at startup to catch missing or invalid settings early

WARNING

  • Avoid hardcoding configuration keys throughout your application - centralize configuration access
  • Be cautious with sensitive data - use User Secrets for development and secure storage for production

Troubleshooting Common Issues

If you encounter problems accessing configuration values:

  1. Ensure appsettings.json is properly formatted with valid JSON
  2. Verify the file properties - "Copy to Output Directory" should be set to "Copy if newer"
  3. Check environment-specific appsettings - values may be overridden by appsettings.Development.json

Conclusion

The ASP.NET Core 6+ minimal hosting model provides straightforward access to configuration through the WebApplicationBuilder.Configuration property. By using direct value access, section binding, or the Options pattern, you can effectively utilize appsettings.json values throughout your application while maintaining clean, maintainable code.

The key is choosing the right approach for your scenario:

  • Simple values: Use direct access with builder.Configuration["Key"]
  • Related settings: Use section binding with strongly-typed classes
  • Service configuration: Use the Options pattern with dependency injection