dotnet / dotnet/aspnetcore

`WebApplicationBuilder.Environment`, `WebApplication.Environment`, and DI-resolved `IHostEnvironment` represent different environment objects

Open
#67,629 1 comment 0 reactions 0 assignees View on GitHub
area-hosting feature-minimal-hosting
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

When using `WebApplication.CreateBuilder(args)`, the environment object exposed by:

- `builder.Environment`
- `app.Environment`
- `app.Services.GetRequiredService()`

does not appear to be a single consistent runtime environment object.

In the minimal repro below, mutating:

```csharp
builder.Environment.ApplicationName = "manual-override";
```

updates `builder.Environment.ApplicationName` and `app.Environment.ApplicationName`, but the `IHostEnvironment` resolved from DI after `Build()` still returns the original application name.

This is surprising and appears inconsistent:

- `builder.Environment` is one object
- `app.Environment` is the same object instance as `builder.Environment`
- `app.Services.GetRequiredService()` is a different object instance

`ApplicationName` is only the easiest visible symptom. The deeper issue is that the framework surfaces what looks like "the application environment" through multiple APIs, but those APIs are not backed by one shared object.

### Expected Behavior

One of the following should be true:

1. `builder.Environment`, `app.Environment`, and DI-resolved `IHostEnvironment` should represent the same underlying runtime environment object.
2. Or the framework should clearly document that these are distinct environment objects with potentially different state, and mutations to one do not affect the others.

Today the application can observe two different environment instances through normal framework APIs without any obvious indication that they are distinct.

### Steps To Reproduce

```csharp
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Hosting;

const string ManualApplicationName = "manual-override";

var builder = WebApplication.CreateBuilder(args);

Console.WriteLine(
"[diag] builder.Environment before set: ApplicationName={0}, Hash={1}",
builder.Environment.ApplicationName,
RuntimeHelpers.GetHashCode(builder.Environment));

builder.Environment.ApplicationName = ManualApplicationName;

Console.WriteLine(
"[diag] builder.Environment after set: ApplicationName={0}, Hash={1}",
builder.Environment.ApplicationName,
RuntimeHelpers.GetHashCode(builder.Environment));

var app = builder.Build();
var resolvedHostEnvironment = app.Services.GetRequiredService();

Console.WriteLine(
"[diag] app.Environment after build: ApplicationName={0}, Hash={1}",
app.Environment.ApplicationName,
RuntimeHelpers.GetHashCode(app.Environment));

Console.WriteLine(
"[diag] app.Services IHostEnvironment: ApplicationName={0}, Hash={1}",
resolvedHostEnvironment.ApplicationName,
RuntimeHelpers.GetHashCode(resolvedHostEnvironment));

app.Run();
```

Example output:

```text
[diag] builder.Environment before set: ApplicationName=application-name-repro, Hash=59941933
[diag] builder.Environment after set: ApplicationName=manual-override, Hash=59941933
[diag] app.Environment after build: ApplicationName=manual-override, Hash=59941933
[diag] app.Services IHostEnvironment: ApplicationName=application-name-repro, Hash=57434139
```

This shows:

- `builder.Environment` and `app.Environment` share the same hash / instance
- the DI-resolved `IHostEnvironment` is a different object
- the two environment objects can diverge in observable state

### Exceptions (if any)

_No response_

### .NET Version

10.0.203

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the WebApplication.CreateBuilder(args) repro and inspect how builder.Environment, app.Environment, and the DI-resolved IHostEnvironment are created and exposed during Build(). Compare the observable instances and state, then determine whether the intended result is shared state or documentation of distinct objects; done requires an agreed behavior and regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.