From 7e00613f8e534665b2c3ba0140e91d17d89f8ab5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 23:31:10 +0000 Subject: [PATCH 1/6] Initial plan From 09e5de041db2eb352a4feb974b90c652b9cd8f66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:06:26 +0000 Subject: [PATCH 2/6] Add DIM for IHostBuilder.UseServiceProviderFactory(Func overload) to prevent TypeLoadException Adds a default interface method (DIM) for the Func-based UseServiceProviderFactory overload in IHostBuilder for .NET Core targets. This prevents TypeLoadException when loading old IHostBuilder implementations (e.g., from Microsoft.Extensions.Hosting v2.2.0.0) that only implement the non-Func overload on .NET 11+. The DIM is not added for .NET Standard or .NET Framework targets since we don't need backward compat shims there. Fixes: TypeLoadException in NukedBit.NRepo and other tools using old Hosting assemblies. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/186bed25-e477-43a0-ab1f-660ba8bc7769 Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com> --- ...crosoft.Extensions.Hosting.Abstractions.cs | 4 ++ .../src/IHostBuilder.cs | 5 ++ .../tests/HostBuilderContextTests.cs | 51 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs index dc256ff6466f30..119fef64f61147 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs @@ -123,7 +123,11 @@ public partial interface IHostBuilder Microsoft.Extensions.Hosting.IHostBuilder ConfigureHostConfiguration(System.Action configureDelegate); Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(System.Action configureDelegate); Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory factory) where TContainerBuilder : notnull; +#if NET + Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(System.Func> factory) where TContainerBuilder : notnull { throw null; } +#else Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(System.Func> factory) where TContainerBuilder : notnull; +#endif } public partial interface IHostedLifecycleService : Microsoft.Extensions.Hosting.IHostedService { diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs index 4d3cd6016bcd84..dcd5267c8a8bd4 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs @@ -59,7 +59,12 @@ public interface IHostBuilder /// The type of builder. /// The factory to register. /// The same instance of the for chaining. +#if NET + IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull + => throw new NotSupportedException($"The type '{GetType()}' does not support '{nameof(UseServiceProviderFactory)}' with a context-based factory. Override this method to provide an implementation."); +#else IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull; +#endif /// /// Enables configuring the instantiated dependency container. This can be called multiple times and diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs index 44903b3aca6760..9be1ee1b714e42 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Xunit; @@ -102,4 +103,54 @@ private class TestHostEnvironment : IHostEnvironment public IFileProvider ContentRootFileProvider { get; set; } = null!; } } + +#if NET + /// + /// Tests the default interface method (DIM) for . + /// This DIM ensures old IHostBuilder implementations (e.g., from Microsoft.Extensions.Hosting 2.2.0.0) can be + /// loaded without TypeLoadException on .NET even though they only implement the non-Func overload. + /// + public class IHostBuilderDefaultInterfaceMethodTests + { + [Fact] + public void UseServiceProviderFactory_FuncOverload_WithoutOverride_ThrowsNotSupportedException() + { + IHostBuilder builder = new MinimalHostBuilder(); + Assert.Throws(() => builder.UseServiceProviderFactory( + _ => new MinimalServiceProviderFactory())); + } + + [Fact] + public void UseServiceProviderFactory_NonFuncOverload_WithoutDIM_CanBeImplementedAndCalled() + { + IHostBuilder builder = new MinimalHostBuilder(); + // This overload does not have a DIM and the MinimalHostBuilder provides its own implementation. + Assert.Same(builder, builder.UseServiceProviderFactory(new MinimalServiceProviderFactory())); + } + + /// + /// Simulates an old IHostBuilder implementation (e.g., from v2.2) that only implements the + /// non-Func overload of UseServiceProviderFactory. The Func overload falls back to the DIM. + /// + private class MinimalHostBuilder : IHostBuilder + { + public IDictionary Properties { get; } = new Dictionary(); + + public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) => this; + public IHostBuilder ConfigureContainer(Action configureDelegate) => this; + public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) => this; + public IHostBuilder ConfigureServices(Action configureDelegate) => this; + public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull => this; + // Note: UseServiceProviderFactory(Func<...>) is intentionally not overridden here; + // the DIM on IHostBuilder provides the fallback implementation. + public IHost Build() => null!; + } + + private class MinimalServiceProviderFactory : IServiceProviderFactory + { + public IServiceCollection CreateBuilder(IServiceCollection services) => services; + public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) => null!; + } + } +#endif } From 397ad86546c1fd1b4b2bd1197ba7c9efa1b6dea9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:21:44 +0000 Subject: [PATCH 3/6] Address code review feedback: add null guard and exception XML doc to DIM Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/186bed25-e477-43a0-ab1f-660ba8bc7769 Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com> --- .../src/IHostBuilder.cs | 6 +++++- .../tests/HostBuilderContextTests.cs | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs index dcd5267c8a8bd4..bb6efca16ec0e2 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs @@ -59,9 +59,13 @@ public interface IHostBuilder /// The type of builder. /// The factory to register. /// The same instance of the for chaining. + /// The implementing type does not support the context-based factory overload. #if NET IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull - => throw new NotSupportedException($"The type '{GetType()}' does not support '{nameof(UseServiceProviderFactory)}' with a context-based factory. Override this method to provide an implementation."); + { + ArgumentNullException.ThrowIfNull(factory); + throw new NotSupportedException($"The type '{GetType()}' does not support '{nameof(UseServiceProviderFactory)}' with a context-based factory. Override this method to provide an implementation."); + } #else IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull; #endif diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs index 9be1ee1b714e42..2e53a5c52f466e 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs @@ -120,6 +120,13 @@ public void UseServiceProviderFactory_FuncOverload_WithoutOverride_ThrowsNotSupp _ => new MinimalServiceProviderFactory())); } + [Fact] + public void UseServiceProviderFactory_FuncOverload_WithNullFactory_ThrowsArgumentNullException() + { + IHostBuilder builder = new MinimalHostBuilder(); + Assert.Throws(() => builder.UseServiceProviderFactory(null!)); + } + [Fact] public void UseServiceProviderFactory_NonFuncOverload_WithoutDIM_CanBeImplementedAndCalled() { From dd524cb9bc4ed3754c665add13480f908c19581c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:53:39 +0000 Subject: [PATCH 4/6] Remove DIM body from ref assembly; keep abstract to require new implementations to override Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bb09ea95-ca82-4973-92b7-3d829aa23711 Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com> --- .../ref/Microsoft.Extensions.Hosting.Abstractions.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs index 119fef64f61147..dc256ff6466f30 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs @@ -123,11 +123,7 @@ public partial interface IHostBuilder Microsoft.Extensions.Hosting.IHostBuilder ConfigureHostConfiguration(System.Action configureDelegate); Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(System.Action configureDelegate); Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory factory) where TContainerBuilder : notnull; -#if NET - Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(System.Func> factory) where TContainerBuilder : notnull { throw null; } -#else Microsoft.Extensions.Hosting.IHostBuilder UseServiceProviderFactory(System.Func> factory) where TContainerBuilder : notnull; -#endif } public partial interface IHostedLifecycleService : Microsoft.Extensions.Hosting.IHostedService { From 6b0dcd135d2a8cc96f6b4f569beb9eb3c88f5055 Mon Sep 17 00:00:00 2001 From: "Eric St. John" Date: Mon, 20 Apr 2026 20:25:58 -0700 Subject: [PATCH 5/6] Fix test --- .../tests/HostBuilderContextTests.cs | 3 ++- .../Microsoft.Extensions.Hosting.Abstractions.Tests.csproj | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs index 2e53a5c52f466e..c1962043865499 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs @@ -124,7 +124,8 @@ public void UseServiceProviderFactory_FuncOverload_WithoutOverride_ThrowsNotSupp public void UseServiceProviderFactory_FuncOverload_WithNullFactory_ThrowsArgumentNullException() { IHostBuilder builder = new MinimalHostBuilder(); - Assert.Throws(() => builder.UseServiceProviderFactory(null!)); + Func>? factory = null; + Assert.Throws(() => builder.UseServiceProviderFactory(factory)); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/Microsoft.Extensions.Hosting.Abstractions.Tests.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/Microsoft.Extensions.Hosting.Abstractions.Tests.csproj index ca92d0164b5fbd..6e3037bad5e40a 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/Microsoft.Extensions.Hosting.Abstractions.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/Microsoft.Extensions.Hosting.Abstractions.Tests.csproj @@ -7,10 +7,7 @@ - - - - + From 885a50dfa4bd6732ddc1e39e3ba22aa937a94d04 Mon Sep 17 00:00:00 2001 From: "Eric St. John" Date: Mon, 20 Apr 2026 20:44:31 -0700 Subject: [PATCH 6/6] Apply feedback --- .../src/IHostBuilder.cs | 1 - .../tests/HostBuilderContextTests.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs index bb6efca16ec0e2..dedaf5d3be0874 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostBuilder.cs @@ -59,7 +59,6 @@ public interface IHostBuilder /// The type of builder. /// The factory to register. /// The same instance of the for chaining. - /// The implementing type does not support the context-based factory overload. #if NET IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull { diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs index c1962043865499..677bf5a0561e2a 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/tests/HostBuilderContextTests.cs @@ -125,7 +125,7 @@ public void UseServiceProviderFactory_FuncOverload_WithNullFactory_ThrowsArgumen { IHostBuilder builder = new MinimalHostBuilder(); Func>? factory = null; - Assert.Throws(() => builder.UseServiceProviderFactory(factory)); + AssertExtensions.Throws("factory", () => builder.UseServiceProviderFactory(factory)); } [Fact]