[Aspire.Hosting.Blazor] Generated Gateway.cs crashes with circular DI dependency (UseOtlpExporter + AddStandardResilienceHandler) on .NET 11 preview
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Description
The auto-generated `Gateway.cs` script (emitted by `Aspire.Hosting.Blazor`) crashes on startup with an unhandled `System.InvalidOperationException: A circular dependency was detected for the service of type 'Microsoft.Extensions.Logging.ILoggerFactory'` when running on .NET 11 preview 6 nightly (11.0.100-preview.6.26316.109).
## Root Cause
`Gateway.cs` registers both:
1. `ConfigureHttpClientDefaults(http => http.AddStandardResilienceHandler())` (line ~119)
2. `builder.Services.AddOpenTelemetry().UseOtlpExporter()` (line ~172)
`UseOtlpExporter()` enables `IHttpClientFactory` integration internally (`TryEnableIHttpClientFactoryIntegration`). When the factory creates the exporter's HttpClient, it applies the default resilience handler. The resilience handler's `TelemetryOptions` needs `ILoggerFactory`. But `ILoggerFactory` depends on the OTel `LoggerProviderSdk` which depends on the `OtlpLogExporter` which needs `IHttpClientFactory` → **circular**.
### Full circular chain:
```
ILoggerFactory
→ LoggerProviderSdk (OTel)
→ OtlpLogExporter
→ IHttpClientFactory
→ AddStandardResilienceHandler
→ TelemetryOptions (Polly)
→ ILoggerFactory ← CIRCLE
```
## Reproduction
1. Create a Blazor Standalone project with Aspire hosting (`AddBlazorWasmProject` + `AddBlazorGateway` + `WithOtlpExporter`)
2. Use .NET 11 preview 6 SDK (11.0.100-preview.6.26316.109)
3. Use `Aspire.Hosting.Blazor 13.4.5-preview.1.26316.12`
4. Run the AppHost
5. Gateway resource exits with code -532462766 (0xE0434352 = unhandled CLR exception)
## Environment
- .NET SDK: 11.0.100-preview.6.26316.109
- Aspire SDK: 13.4.5
- Aspire.Hosting.Blazor: 13.4.5-preview.1.26316.12
- OS: Windows 11
- Packages in generated Gateway.cs:
- Microsoft.Extensions.Http.Resilience@10.6.0
- OpenTelemetry.Exporter.OpenTelemetryProtocol@1.15.3
## Workaround
Replace `UseOtlpExporter()` with per-signal OTLP export in the generated `Gateway.cs`:
```csharp
// Instead of:
// builder.Services.AddOpenTelemetry().UseOtlpExporter();
// Use per-signal export (doesn't use IHttpClientFactory):
builder.Services.AddOpenTelemetry()
.WithMetrics(m => m.AddOtlpExporter())
.WithTracing(t => t.AddOtlpExporter());
```
Per-signal `AddOtlpExporter()` (on `MeterProviderBuilder`/`TracerProviderBuilder`) doesn't enable `IHttpClientFactory` integration, avoiding the circular dependency.
## Suggested Fix
In the Gateway.cs template emitted by `Aspire.Hosting.Blazor`, either:
1. Replace `UseOtlpExporter()` with per-signal `AddOtlpExporter()` calls
2. Or exclude the OTLP exporter's named HttpClient from the resilience handler defaults
3. Or register the resilience handler AFTER `UseOtlpExporter()` with appropriate filtering
## Stack Trace (abbreviated)
```
System.InvalidOperationException: A circular dependency was detected for the service of type 'Microsoft.Extensions.Logging.ILoggerFactory'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(...)
...
at Polly.PollyServiceCollectionExtensions.<>c.b__7_0(TelemetryOptions options, IServiceProvider serviceProvider)
...
at OpenTelemetry.Exporter.OtlpExporterOptionsExtensions.<>c__DisplayClass9_0.b__0()
...
at OpenTelemetry.Exporter.OtlpLogExporter..ctor(...)
...
at Program.$(String[] args) in Gateway.cs:line 47
```
Contributor guide
Research direction
Locate the Gateway.cs template emitted by Aspire.Hosting.Blazor and reproduce the startup failure with the listed .NET 11 preview and Aspire versions. Inspect the registrations for AddStandardResilienceHandler and UseOtlpExporter, then verify that the generated gateway starts without the circular ILoggerFactory dependency while retaining OTLP export.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100