Skip to content

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Jan 9, 2026

Backport of #64953 to release/8.0

/cc @halter73 @copilot

Add .slnx fallback for TestHost content root discovery

Description

This pull request adds .slnx fallback support to the test-only UseSolutionRelativeContentRoot method in the Microsoft.AspNetCore.TestHost. When searching for solution files with the *.sln pattern, the method now falls back to searching for .slnx files if no .sln file is found, preventing the InvalidOperationException that would previously occur before test developers can easily call UseSolutionRelativeContentRoot themselves.

This has already been fixed in .NET 10 by #61305, but that included public API changes, so this is not a direct backport. Unlike, the .NET 10 change that treats .slnx and .sln equivalently by default, this change will only fall back to looking for an *.slnx file if the test server would otherwise throw an InvalidOperationException due to not being able to find an ".sln" file.

Fixes #61304 in .NET 8.

Customer Impact

#61304 has gotten a lot of attention, because it is a pain point for migrating solution from .sln to .slnx.

System.InvalidOperationException : Solution root could not be located using application root C:\Project\Path\bin\Debug\net9.0\

Stack Trace:
 at Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(IWebHostBuilder builder, String solutionRelativePath, String applicationBasePath, String solutionName)
 at Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(IWebHostBuilder builder, String solutionRelativePath, String solutionName)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.SetContentRoot(IWebHostBuilder builder)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.<ConfigureHostBuilder>b__22_0(IWebHostBuilder webHostBuilder)
 at Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions.ConfigureWebHost(IHostBuilder builder, Func`3 createWebHostBuilder, Action`1 configure, Action`1 configureWebHostBuilder)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.ConfigureHostBuilder(IHostBuilder hostBuilder)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient()

Test projects that worked fine with an .sln-based solution throw after migrating to *.slnx before developers even get access to the IWebHostBuilder, so they don't get the opportunity to easily manually reconfigure the content root by calling UseSolutionRelativeContentRoot with "*.slnx". Instead, the current general workaround is fragile and unintuitive:

public static class MvcTestingAppManifestHelper
{
    private const string ManifestFileName = "MvcTestingAppManifest.json";
    private static readonly JsonSerializerOptions JsonSerializerOptions = new() { WriteIndented = true };

    public static void AddAssemblyToManifest(Assembly assembly)
    {
        if (!File.Exists(ManifestFileName))
        {
            return;
        }

        // The manifest file is a dictionary of Assembly.FullName to ContentRoot.
        var manifest = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(ManifestFileName))!;

        // Internally, the tilde is used to translate the content root to the AppContext.BaseDirectory.
        if (manifest.TryAdd(assembly.FullName!, "~"))
        {
            File.WriteAllText(ManifestFileName, JsonSerializer.Serialize(manifest, JsonSerializerOptions));
        }
    }
}

#61304 (comment)

Regression?

  • Yes
  • No

Not technically, but you could consider it a regression in behavior when migrating from .sln to .slnx solutions.

Risk

  • High
  • Medium
  • Low

This change only has an impact if starting the test server would otherwise throw an InvalidOperationException early during initialization due to not being able to find an ".sln" file.

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

Copilot AI and others added 2 commits January 9, 2026 23:47
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
@github-actions github-actions bot requested a review from halter73 as a code owner January 9, 2026 23:47
@dotnet-policy-service dotnet-policy-service bot added this to the 8.0.x milestone Jan 9, 2026
@halter73 halter73 added the Servicing-consider Shiproom approval is required for the issue label Jan 10, 2026
@dotnet-policy-service
Copy link
Contributor

Hi @@github-actions[bot]. Please make sure you've updated the PR description to use the Shiproom Template. Also, make sure this PR is not marked as a draft and is ready-to-merge.

To learn more about how to prepare a servicing PR click here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Servicing-consider Shiproom approval is required for the issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants