Description
Description
I encountered an issue with IServiceProvider.GetKeyedServices() in .NET 8 and .NET 9 where it creates new instances of Singleton services instead of returning existing ones when you mix using of KeyedService.AnyKey and of specific keys.
Reproduction Steps
- Create a new .NET project.
- Add the following dependencies:
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" /> <PackageReference Include="MSTest" Version="3.8.2" />
- Add the following test code:
[TestClass] public sealed class Test1 { [TestMethod] public void TestMethod1() { var services = new ServiceCollection(); services.AddKeyedSingleton<TestService>("key1"); services.AddKeyedSingleton<TestService>("key2"); services.AddKeyedSingleton<TestService>("key3"); var serviceProvider = services.BuildServiceProvider(); var allInstances = serviceProvider.GetKeyedServices<TestService>(KeyedService.AnyKey).ToArray(); var service1 = serviceProvider.GetKeyedService<TestService>("key1"); var service2 = serviceProvider.GetKeyedService<TestService>("key2"); var service3 = serviceProvider.GetKeyedService<TestService>("key3"); Assert.AreEqual(3, allInstances.Length); Assert.Contains(service1, allInstances); Assert.Contains(service2, allInstances); Assert.Contains(service3, allInstances); } } public class TestService { public string Id { get; set; } = Guid.NewGuid().ToString(); public override bool Equals(object obj) { if (obj is TestService other) { return Id == other.Id; } return false; } public override int GetHashCode() { return Id.GetHashCode(); } }
Expected behavior
The test should pass, and GetKeyedService<TestService>("key1")
should return the same instance that was created when calling GetKeyedServices<TestService>(KeyedService.AnyKey)
.
Actual behavior
The test fails because GetKeyedService<TestService>("key1")
creates a new instance instead of returning the existing one. This issue persists regardless of the order in which the services are requested: when calling GetKeyedServices<TestService>("key1")
first, it also creates new instances when calling GetKeyedServices<TestService>(KeyedService.AnyKey)
instead of returning the already created ones.
Regression?
No response
Known Workarounds
It works if you register your singletons in the following way:
services.AddKeyedSingleton("key1", (sp, key) => key== KeyedService.AnyKey ? sp.GetKeyedService<TestService>("key1") : new TestService() );
services.AddKeyedSingleton("key2", (sp, key) => key == KeyedService.AnyKey ? sp.GetKeyedService<TestService>("key2") : new TestService());
services.AddKeyedSingleton("key3", (sp, key) => key == KeyedService.AnyKey ? sp.GetKeyedService<TestService>("key3") : new TestService());
However, I'm not sure if this is correct workaround and if won't cause any side effects.
Configuration
.NET 8 and .NET 9
Microsoft.Extensions.Hosting Version: 9.0.3
Microsoft.Extensions.DependencyInjection Version: 9.0.3
Other information
A similar issue was previously fixed for services registered with specific instances using IServiceCollection.AddKeyedSingleton(object? serviceKey, TService implementationInstance) in #95582. However, the problem with services created by the DI container itself remains