Skip to content

Replace WebHostBuilder with HostBuilder pattern in MVC folder #62703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jul 14, 2025

Summary

This PR replaces all usages of WebHostBuilder with the modern HostBuilder and ConfigureWebHost pattern across the MVC folder, updating 24 files to follow current ASP.NET Core hosting best practices.

Part of #20964

Changes Made

Pattern Transformation:

// Before
var host = new WebHostBuilder()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseStartup<Startup>()
    .UseKestrel()
    .Build();

// After  
using var host = new HostBuilder()
    .ConfigureWebHost(webHostBuilder =>
    {
        webHostBuilder
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseKestrel();
    })
    .Build();

Files Updated (24 total):

  • Samples (1): MvcSandbox/Startup.cs
  • Performance Benchmarks (3): BasicApi, BasicViews, RazorRendering
  • Test Website Program.cs (7): BasicWebSite, CorsWebSite, FormatterWebSite, RazorPagesWebSite, RazorWebSite, RoutingWebSite, SecurityWebSite, VersioningWebSite
  • Test Website Startup.cs (13): SimpleWebSite, FilesWebSite, ApiExplorerWebSite, ApplicationModelWebSite, ControllersFromServicesWebSite, ErrorPageMiddlewareWebSite, TagHelpersWebSite, XmlFormattersWebSite, HtmlGenerationWebSite (2 files), RazorBuildWebSite (2 files)

Key Changes Applied

Added using statements: using Microsoft.Extensions.Hosting;
Updated variable declarations: var hostusing var host
Renamed methods: CreateWebHostBuilder()CreateHost()
Changed return types: IWebHostBuilderIHostBuilder
Wrapped configuration: new WebHostBuilder()new HostBuilder().ConfigureWebHost(...)
Preserved all configurations: All original .UseKestrel(), .UseStartup<>(), .UseConfiguration(), .UseStaticWebAssets(), etc. methods maintained

Testing

  • All transformations follow the exact pattern specified in the requirements
  • Original functionality preserved - all configuration calls moved inside ConfigureWebHost lambda
  • Special configurations like .UseStaticWebAssets() in TagHelpersWebSite correctly handled
  • Complex configurations with local variables (like ConfigurationBuilder) properly maintained

Verification

  • ✅ 0 remaining new WebHostBuilder() usages in MVC folder
  • ✅ 24 files successfully transformed with new HostBuilder() pattern
  • ✅ All files compile with proper using statements and method signatures

This pull request was created as a result of the following prompt from Copilot chat.

Remove all usages of WebHostBuilder in the MVC folder and replace them with HostBuilder and ConfigureWebHost. Specifically, for each occurrence of:

Before:
var host = new WebHostBuilder()
.Configure(app => {
//...
})
.Build();

Change to:
using var host = new HostBuilder()
.ConfigureWebHost(webHostBuilder => {
webHostBuilder
.Configure(app => {
//...
});
})
.Build();

Ensure all usages in the MVC folder follow this new pattern, updating variable declarations and method chains as needed.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@Copilot Copilot AI changed the title [WIP] Replace WebHostBuilder with HostBuilder and ConfigureWebHost in MVC folder Replace WebHostBuilder with HostBuilder pattern in MVC folder Jul 14, 2025
@Copilot Copilot AI requested a review from BrennanConroy July 14, 2025 06:08
Copilot finished work on behalf of BrennanConroy July 14, 2025 06:08
@@ -25,7 +25,6 @@ protected override void Initialize(TestContext context, MethodInfo methodInfo, o
{
base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper);
Factory = new MvcTestFixture<RazorBuildWebSite.Startup>(LoggerFactory)
.WithWebHostBuilder(b => b.UseStartup<RazorBuildWebSite.Startup>())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the Startup type is the same as the one in the test fixture, we end up running both startups which both register a file provider and ends up messing with the cache-ability of razor views.

So removing this fixes the 2 quarantined tests below, and I'll mark #56553 as test-fixed.

We also might want to consider changing GenericWebHostBuilder to detect the same startup type so it doesn't run it multiple times?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find! Handling this in GenericWebHostBuilder sounds like a good idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit.

/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> from the bootstrapped application.</param>
/// <returns></returns>
protected virtual TestServer CreateServer(IServiceProvider serviceProvider) => new(serviceProvider);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this and consider it later if we want, as it introduces a new API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm inclined to keep it even though introducing a new API in an obsoletion pass feels funky since we don't expose any other nice ways for users to customize TestServer with a finalized DI container and we've gotten feedback in the past about how hard it is to plugin WAF APIs into different points in the minimal host's lifecycle (mostly before and after IHostBuilder.Build, but still).

@@ -1681,11 +1681,11 @@ public void Configure(IWebHostBuilder builder)
// This check is required because MVC still uses the
// IWebHostEnvironment instance before the container is baked
#pragma warning disable CS0618 // Type or member is obsolete
var heDescriptor = services.SingleOrDefault(s => s.ServiceType == typeof(IHostingEnvironment));
var heDescriptor = services.FirstOrDefault(s => s.ServiceType == typeof(IHostingEnvironment));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be LastOrDefault?


_builder.ConfigureServices((context, services) =>
{
// Run this delegate if the startup type matches
if (object.ReferenceEquals(_startupObject, startupType))
if (_builder.Properties.TryGetValue(_startupConfigName, out var startupObject) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug introduced by #24144 that allowed multiple startups to run if they spanned different IWebHostBuilder instances. See test MultipleConfigureWebHostCallsWithUseStartupLastWins below for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crazy

@BrennanConroy BrennanConroy marked this pull request as ready for review July 15, 2025 20:57
@BrennanConroy BrennanConroy requested review from a team and halter73 as code owners July 15, 2025 20:57
@BrennanConroy BrennanConroy added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates and removed Attention: Shared Code Modified labels Jul 15, 2025
services.TryAddTransient<HtmlEncoder, HtmlTestEncoder>();
services.TryAddTransient<JavaScriptEncoder, JavaScriptTestEncoder>();
services.TryAddTransient<UrlEncoder, UrlTestEncoder>();
services.AddTransient<HtmlEncoder, HtmlTestEncoder>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for this change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, forgot about this one.

So the two hosts handle ordering differently.

var host = new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureServices(services =>
    {
        services.TryAddSingleton<Bar>(new Bar() { Value = 10 });
    })
    .Build();

var host2 = new HostBuilder()
    .ConfigureWebHost(webhostbuilder =>
        webhostbuilder
            .UseKestrel()
            .UseStartup<Startup>()
    )
    .ConfigureWebHost(webhostbuilder =>
    {
        webhostbuilder.ConfigureServices(services =>
        {
            services.TryAddSingleton<Bar>(new Bar() { Value = 10 });
        });
    }).Build();

Console.WriteLine(host.Services.GetRequiredService<Bar>().Value);
Console.WriteLine(host2.Services.GetRequiredService<Bar>().Value);

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddSingleton<Bar>(new Bar() { Value = 30 });
    }
}

public class  Bar
{
    public int Value { get; set; }
}

This prints:

10
30

It's too late to change eithers order, but HostBuilder does feel a bit more natural since it does things more in order of how the code is ordered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants