Configuring Services and Middleware in ASP.NET 6+ Without Startup.cs
Since .NET 6, Microsoft has streamlined the application startup process by unifying Program.cs
and Startup.cs
into a single file. This change simplifies the project structure but can be confusing for developers migrating from earlier versions.
The New Program.cs Structure
In .NET 6+, the Program.cs
file now handles both service configuration and middleware pipeline setup:
var builder = WebApplication.CreateBuilder(args);
// Service configuration (formerly in Startup.ConfigureServices)
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
// Middleware configuration (formerly in Startup.Configure)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Adding DbContext in .NET 6+
To register your DbContext in the new structure:
var builder = WebApplication.CreateBuilder(args);
// Add DbContext with SQL Server
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
// Add other services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// ... rest of middleware configuration
Registering Different Service Lifetimes
The same service registration patterns from Startup.cs still apply:
// Transient services (created each time requested)
builder.Services.AddTransient<IEmailService, EmailService>();
// Scoped services (created once per request)
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Singleton services (created first time requested)
builder.Services.AddSingleton<ICacheService, CacheService>();
Organizing with Extension Methods
For better code organization, you can create extension methods:
public static class ServiceExtensions
{
public static void ConfigureDatabase(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
}
public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// JWT configuration
});
}
}
Then use them in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureDatabase(builder.Configuration);
builder.Services.ConfigureAuthentication(builder.Configuration);
Recreating the Startup Class Pattern
If you prefer the traditional Startup class structure, you can recreate it:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers();
}
public void Configure(WebApplication app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
}
}
Use it in Program.cs:
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, app.Environment);
app.Run();
Migration Tips
TIP
When migrating from .NET 5 or earlier:
- Move service registrations from
Startup.ConfigureServices()
tobuilder.Services
calls - Move middleware configuration from
Startup.Configure()
to aftervar app = builder.Build()
- Update references from
IConfiguration
tobuilder.Configuration
WARNING
Ensure proper ordering of middleware calls. The sequence matters for authentication, authorization, and routing middleware.
The new unified Program.cs approach reduces boilerplate code while maintaining the same functionality. Choose the organization method that best fits your team's preferences and project complexity.