Skip to content
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

IHostBuilder.Build() times out when debugging startup with minimal hosting and WebApplicationFactory<T> #33886

Closed
martincostello opened this issue Jun 27, 2021 · 5 comments · Fixed by dotnet/runtime#54791
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-minimal-hosting
Milestone

Comments

@martincostello
Copy link
Member

Describe the bug

When debugging startup paths when using WebApplicationBuilder for hosting and WebApplicationFactory<T> for testing, if more than 5 seconds elapses (which is likely when debugging in an IDE), then the IHost will fail to build and thow an InvalidOperationException with the message Unable to build IHost.

This appears to be because no explicit timeout is passed for the waitTimeout parameter of the call to ResolveHostFactory() here:

var factory = HostFactoryResolver.ResolveHostFactory(
typeof(TEntryPoint).Assembly,
stopApplication: false,
configureHostBuilder: deferredHostBuilder.ConfigureHostBuilder,
entrypointCompleted: deferredHostBuilder.EntryPointCompleted);

This leads to the default timeout of 5 seconds being used.

An appropriate higher (or infinite) timeout should be used when debugging so that after debugging startup code the host will start successfully.

To Reproduce

Run the following unit test with the debugger attached:

[Fact]
public static void Startup_Does_Not_Time_Out()
{
    // Arrange
    using var fixture = new WebApplicationFactory<FakeEntryPoint>().WithWebHostBuilder(builder =>
    {
        builder.ConfigureServices(_ =>
        {
            if (Debugger.IsAttached)
            {
                Thread.Sleep(TimeSpan.FromSeconds(6));
            }
        });
    });

    // Act
    using var client = fixture.CreateClient();
}

App code:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;

var builder = Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseRouting();
app.UseEndpoints((endpoints) =>
{
    endpoints.MapGet("/api", async (IHostEnvironment environment) =>
    {
        await Task.CompletedTask;

        return new
        {
            environment = environment.EnvironmentName
        };
    });
});

app.Run();

namespace WebApplication
{
    public class FakeEntryPoint
    {
    }
}

Exceptions (if any)

 WebApplication.Tests.UnitTest1.Startup_Does_Not_Time_Out
   Source: UnitTest1.cs line 151
   Duration: 5.1 sec

  Message: 
    System.InvalidOperationException : Unable to build IHost

  Stack Trace: 
    Testing.dll:token 0x60000aa+0x4b
    Testing.dll:token 0x60000b2+0x41
    Testing.dll:token 0x6000008+0x0
    Testing.dll:token 0x6000028+0x0
    Testing.dll:token 0x6000082+0x0
    Testing.dll:token 0x600001d+0x13
    Testing.dll:token 0x600001c+0x7e
    Testing.dll:token 0x600002c+0x0
    Testing.dll:token 0x600002e+0x0
    Testing.dll:token 0x600002b+0x0
    Testing.dll:token 0x600002a+0x0
    UnitTest1.Startup_Does_Not_Time_Out() line 166
    --- End of stack trace from previous location ---

Further technical details

  • ASP.NET Core 6.0.0-preview.6.21323.4
  • .NET SDK 6.0.100-preview.6.21324.1
  • Visual Studio 2022 17.0.0 Preview 1.1
@davidfowl
Copy link
Member

Yes this is a good catch, I've been meaning to make this not timeout at all when the debugger is attached (Timeout.InfiniteTimeSpan)

@martincostello
Copy link
Member Author

I'm digging into #33876 locally at the moment (which is what prompted this issue) and I have the following in it locally.

var deferredHostBuilder = new DeferredHostBuilder();

var waitTimeout = new TimeSpan?();

if (Debugger.IsAttached)
{
    waitTimeout = Timeout.InfiniteTimeSpan;
}

// This helper call does the hard work to determine if we can fallback to diagnostic source events to get the host instance
var factory = HostFactoryResolver.ResolveHostFactory(
    typeof(TEntryPoint).Assembly,
    waitTimeout,
    stopApplication: false,
    configureHostBuilder: deferredHostBuilder.ConfigureHostBuilder,
    entrypointCompleted: deferredHostBuilder.EntryPointCompleted);

I wasn't sure what heuristic was preferred for "is it debug time?" in case there was some nuance to it like with "is development?" so didn't suggest a specific fix. If the above is what you'd want to do anyway, I'd be happy to push up a PR as a fix candidate for this.

@davidfowl
Copy link
Member

I would make it in dotnet/runtime

@martincostello
Copy link
Member Author

Cool I'll do that shortly then 👍

martincostello added a commit to martincostello/runtime that referenced this issue Jun 27, 2021
Do not timeout waiting for the host to start when running with
the debugger attached.

Addresses dotnet/aspnetcore#33886.
eerhardt pushed a commit to dotnet/runtime that referenced this issue Jun 27, 2021
Do not timeout waiting for the host to start when running with
the debugger attached.

Addresses dotnet/aspnetcore#33886.
@BrennanConroy
Copy link
Member

Done via dotnet/runtime#54791

@BrennanConroy BrennanConroy added this to the 6.0-preview7 milestone Jun 28, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jul 28, 2021
@amcasey amcasey added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed area-runtime labels Jun 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-minimal-hosting
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants