-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Summary
In .NET 10, HostApplicationBuilder constructs the host, logging pipeline, DI container, and partial configuration immediately upon instantiation, rather than deferring construction until Build() is called. This behavior diverges from the established hosting semantics used in ASP.NET Core and in .NET 6–9 console/worker applications.
This change breaks the expected guarantees of the Builder pattern, violates multiple SOLID principles, and causes observable regressions in logging provider behavior, configuration ordering, and DI extensibility.
Reproduction
csharp
var builder = Host.CreateApplicationBuilder(args);
// Add configuration AFTER builder creation
builder.Configuration.AddJsonFile("mysettings.json");
// Add a custom logging provider that depends on merged configuration
builder.Logging.AddProvider(new MyConfigAwareProvider(builder.Configuration));
// Add Serilog
builder.Host.UseSerilog();
// Build and run
var host = builder.Build();
host.Run();
Observed behavior
Only the default Console/Debug/Trace providers are registered.
Custom providers added after builder creation are ignored or constructed with incomplete configuration.
Serilog replaces the logging pipeline after the host has already been constructed.
Provider ordering becomes brittle and unpredictable.
Expected behavior (based on .NET 6–9 and ASP.NET Core)
Configuration is fully merged before logging is constructed.
Logging providers added during builder configuration are honored.
The host is not constructed until Build() is called.
Provider ordering is deterministic and consistent with ASP.NET Core’s hosting lifecycle.
Impact
This change breaks:
Custom logging providers
Providers that depend on merged configuration
Serilog/NLog/OpenTelemetry integration patterns
Extension libraries that assume deferred host construction
Consistency between console apps and ASP.NET Core apps
The mental model developers have relied on since .NET Core 1.0
✅ Architectural Analysis (SOLID Violations)
- Single Responsibility Principle (SRP)
HostApplicationBuilder now performs multiple responsibilities:
Constructs the host
Constructs logging
Constructs DI
Constructs configuration
This contradicts the purpose of a builder, which should only collect configuration until Build() is invoked.
- Open/Closed Principle (OCP)
Because the host is constructed early:
Logging providers added later are ignored or overwritten
Configuration added later is not applied to logging
DI services added later cannot influence early‑constructed components
The system is no longer open for extension.
- Liskov Substitution Principle (LSP)
HostApplicationBuilder and WebApplicationBuilder share conceptual interfaces but now have different lifecycle semantics:
ASP.NET Core defers construction until Build()
HostApplicationBuilder constructs immediately
This breaks substitutability and violates the expectations of libraries built on the hosting abstractions.
- Interface Segregation Principle (ISP)
The hosting interfaces imply:
Deferred construction
Order‑independent configuration
Predictable initialization
But the implementation performs eager construction, making the interface contract misleading.
- Dependency Inversion Principle (DIP)
High‑level components (logging, configuration, DI) now depend on the low‑level detail of when the builder is instantiated. Initialization order becomes fragile and error‑prone.
✅ Why This Is a Regression
From .NET Core 1.0 through .NET 9.0:
The host was not constructed until Build()
Logging was not constructed until configuration was finalized
DI was not constructed until all services were registered
Provider ordering was deterministic
Console apps and ASP.NET Core apps shared the same hosting semantics
.NET 10 breaks these guarantees without changing the interfaces or documentation that describe them.
Reproduction Steps
Reproduction
csharp
var builder = Host.CreateApplicationBuilder(args);
// Add configuration AFTER builder creation
builder.Configuration.AddJsonFile("mysettings.json");
// Add a custom logging provider that depends on merged configuration
builder.Logging.AddProvider(new MyConfigAwareProvider(builder.Configuration));
// Add Serilog
builder.Host.UseSerilog();
// Build and run
var host = builder.Build();
host.Run();
Observed behavior
Only the default Console/Debug/Trace providers are registered.
Expected behavior
All requested providers are registered.
Actual behavior
Only the default Console/Debug/Trace providers are registered.
Regression?
✅ Why This Is a Regression
From .NET Core 1.0 through .NET 9.0:
The host was not constructed until Build()
Logging was not constructed until configuration was finalized
DI was not constructed until all services were registered
Provider ordering was deterministic
Console apps and ASP.NET Core apps shared the same hosting semantics
.NET 10 breaks these guarantees without changing the interfaces or documentation that describe them.
Known Workarounds
No response
Configuration
.NET 10; works as expected with .NET 8.0.22 and .NET 9.0.11
Other information
✅ Requested Resolution
✅ Option A: Restore deferred construction
Ensure that host, logging, DI, and configuration are not constructed until Build() is called.
✅ Option B: Provide an explicit “eager build” API
If early construction is required for performance reasons, expose it as an opt‑in API rather than the default behavior.
✅ Option C: Document the new lifecycle explicitly
If the new behavior is intentional, update documentation to reflect:
Early construction
Logging provider limitations
Configuration ordering constraints
Divergence from ASP.NET Core hosting semantics
✅ Option D (requested): Add a second HostApplicationBuilder with ASP.NET‑style semantics
Introduce a new builder type (e.g., DeferredHostApplicationBuilder) that:
Restores the deferred‑construction model
Matches ASP.NET Core’s hosting lifecycle
Preserves compatibility with .NET 6–9 patterns
Allows console/worker apps to use the same semantics as web apps
This would allow both models to coexist without breaking existing code or forcing developers into the eager‑construction pipeline.