Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
---
title: "Breaking change: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey"
description: "Learn about the breaking change in .NET 10 where GetKeyedService() and GetKeyedServices() behavior changed when using KeyedService.AnyKey as the lookup key."
ms.date: 11/19/2025
ms.date: 01/26/2026
ai-usage: ai-assisted
---

# Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey

The behavior of the <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedService(System.IServiceProvider,System.Type,System.Object)> and <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedServices(System.IServiceProvider,System.Type,System.Object)> methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the <xref:Microsoft.Extensions.DependencyInjection.KeyedService.AnyKey?displayProperty=nameWithType> registration. Specifically, `GetKeyedService()` now throws an exception when you attempt to resolve a single service using `KeyedService.AnyKey` as the lookup key, and `GetKeyedServices()` (plural) no longer returns `AnyKey` registrations when queried with `KeyedService.AnyKey`.
The behavior of the <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedService(System.IServiceProvider,System.Type,System.Object)> and <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedServices(System.IServiceProvider,System.Type,System.Object)> methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the <xref:Microsoft.Extensions.DependencyInjection.KeyedService.AnyKey?displayProperty=nameWithType> registration. Specifically:

- `GetKeyedService()` now throws an exception when you attempt to resolve a single service using `KeyedService.AnyKey` as the lookup key.
- `GetKeyedServices()` (plural) no longer returns `AnyKey` registrations when queried with `KeyedService.AnyKey`.

## Version introduced

Expand All @@ -32,7 +35,7 @@ Additionally, calling `GetKeyedServices()` with `KeyedService.AnyKey` no longer

```csharp
var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey);
// Returns an empty collection.
// Returns only services that were registered with a specific key.
```

## Type of breaking change
Expand All @@ -41,14 +44,19 @@ This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The previous behavior of `GetKeyedService()` and `GetKeyedServices()` with `KeyedService.AnyKey` was inconsistent with the intended semantics of `AnyKey`. The changes were introduced to ensure that `AnyKey` is treated as a special case and can't be used to resolve a single service, and to prevent `GetKeyedServices()` from returning `AnyKey` registrations when queried with `AnyKey`. These updates improve the predictability and correctness of the `Microsoft.Extensions.DependencyInjection` library's behavior when working with keyed services. For more details, see the [pull request](https://github.com/dotnet/runtime/pull/113137) and the associated [merge commit](https://github.com/dotnet/runtime/commit/deee462fc8421a7e18b8916eb5a5eacb9d09169d).
The previous behavior of `GetKeyedService()` and `GetKeyedServices()` with `KeyedService.AnyKey` was inconsistent with the intended semantics of `AnyKey`. The changes were introduced to:

- Ensure that `AnyKey` is treated as a special case and can't be used to resolve a single service.
- Prevent `GetKeyedServices()` from returning `AnyKey` registrations when queried with `AnyKey`.

These updates improve the predictability and correctness of the `Microsoft.Extensions.DependencyInjection` library's behavior when working with keyed services. For more details, see the [pull request](https://github.com/dotnet/runtime/pull/113137) and the associated [merge commit](https://github.com/dotnet/runtime/commit/deee462fc8421a7e18b8916eb5a5eacb9d09169d).

## Recommended action

If you use `GetKeyedService()` or `GetKeyedServices()` with `KeyedService.AnyKey`, review your code and update it to use specific keys instead of `AnyKey`:

- Replace `GetKeyedService(KeyedService.AnyKey)` calls with specific keys or alternative logic to handle service resolution.
- Replace `GetKeyedServices(KeyedService.AnyKey)` calls with specific keys, or update your logic to enumerate only the services you intend to retrieve.
- Update `GetKeyedService(KeyedService.AnyKey)` calls to pass specific keys, or use alternative logic to handle service resolution.
- Update `GetKeyedServices(KeyedService.AnyKey)` calls to pass specific keys, or use alternative logic to enumerate only the services you intend to retrieve.

## Affected APIs

Expand Down
18 changes: 15 additions & 3 deletions docs/core/extensions/dependency-injection/overview.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Dependency injection
description: Learn how to use dependency injection within your .NET apps. Discover how to define service lifetimes and express dependencies in C#.
ms.date: 10/21/2025
ms.date: 01/26/2026
ms.topic: overview
ai-usage: ai-assisted
---
Expand Down Expand Up @@ -216,7 +216,11 @@ public class ExampleService

### KeyedService.AnyKey property

The <xref:Microsoft.Extensions.DependencyInjection.KeyedService.AnyKey?displayProperty=nameWithType> property provides a special key for working with keyed services. You can register a service using `KeyedService.AnyKey` as a fallback that matches any key. This is useful when you want to provide a default implementation for any key that doesn't have an explicit registration.
The <xref:Microsoft.Extensions.DependencyInjection.KeyedService.AnyKey?displayProperty=nameWithType> property provides a special key for working with keyed services.

#### Service registration

You can register a service using `KeyedService.AnyKey` as a fallback that matches any key. This is useful when you want to provide a default implementation for any key that doesn't have an explicit registration.

:::code language="csharp" source="snippets/anykey/Program.cs" id="FallbackRegistration":::

Expand All @@ -225,8 +229,16 @@ In the preceding example:
- Requesting `ICache` with key `"premium"` returns the `PremiumCache` instance.
- Requesting `ICache` with any other key (like `"basic"` or `"standard"`) creates a new `DefaultCache` using the `AnyKey` fallback.

#### Query for services

You can query for all services that were registered *using a specific key* (that is, not with `KeyedService.AnyKey`), by passing `KeyedService.AnyKey` to <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedServices``1(System.IServiceProvider,System.Object)>.

:::code language="csharp" source="snippets/anykey/Program.cs" id="AnyKeyQuery":::

In the preceding example, calling `GetKeyedServices<T>(KeyedService.AnyKey)` returns only the `PremiumCache` instance since it's the only cache that was registered using a specific key in the [service registration](#service-registration) code example.

> [!IMPORTANT]
> Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an <xref:System.InvalidOperationException> because `AnyKey` is intended as a registration fallback, not as a query key. For more information, see [Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey](../../compatibility/extensions/10.0/getkeyedservice-anykey.md).
> Starting in .NET 10, calling `GetKeyedService()` (singular) with `KeyedService.AnyKey` throws an <xref:System.InvalidOperationException> because `AnyKey` shouldn't be used to resolve a single service. For more information, see [Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey](../../compatibility/extensions/10.0/getkeyedservice-anykey.md).

## See also

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,37 @@ static void FallbackExample()
// Register a specific cache for the "premium" key.
services.AddKeyedSingleton<ICache>("premium", new PremiumCache());

var provider = services.BuildServiceProvider();
ServiceProvider provider = services.BuildServiceProvider();

// Requesting with "premium" key returns PremiumCache.
var premiumCache = provider.GetKeyedService<ICache>("premium");
ICache? premiumCache = provider.GetKeyedService<ICache>("premium");
Console.WriteLine($"Premium key: {premiumCache}");

// Requesting with any other key uses the AnyKey fallback.
var basicCache = provider.GetKeyedService<ICache>("basic");
ICache? basicCache = provider.GetKeyedService<ICache>("basic");
Console.WriteLine($"Basic key: {basicCache}");

var standardCache = provider.GetKeyedService<ICache>("standard");
ICache? standardCache = provider.GetKeyedService<ICache>("standard");
Console.WriteLine($"Standard key: {standardCache}");

/* This example outputs:
*
* Premium key: Premium cache
* Basic key: basic cache
* Standard key: standard cache
*/
// </FallbackRegistration>

// <AnyKeyQuery>
IEnumerable<ICache>? keyedCaches = provider.GetKeyedServices<ICache>(KeyedService.AnyKey);
foreach (ICache cache in keyedCaches)
{
Console.WriteLine($"AnyKey registered cache: {cache}");
}

/* This example outputs:
*
* AnyKey registered cache: Premium cache
*/
// </AnyKeyQuery>
}