Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions docs/core/compatibility/10.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ If you're migrating an app to .NET 10, the breaking changes listed here might af
| Title | Type of change |
|-------|---------------------|
| [BackgroundService runs all of ExecuteAsync as a Task](extensions/10.0/backgroundservice-executeasync-task.md) | Behavioral change |
| [Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey](extensions/10.0/getkeyedservice-anykey.md) | Behavioral change |
| [Null values preserved in configuration](extensions/10.0/configuration-null-values-preserved.md) | Behavioral change |
| [Message no longer duplicated in Console log output](extensions/10.0/console-json-logging-duplicate-messages.md) | Behavioral change |
| [ProviderAliasAttribute moved to Microsoft.Extensions.Logging.Abstractions assembly](extensions/10.0/provideraliasattribute-moved-assembly.md) | Source incompatible |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
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
ai-usage: ai-assisted
---

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

The behavior of the <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetKeyedService(System.IServiceProvider,System.Type,System.Object)> and <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.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`.

## Version introduced

.NET 10

## Previous behavior

Previously, calling `GetKeyedService()` with `KeyedService.AnyKey` returned a service registration associated with `AnyKey`. This behavior was inconsistent with the intended semantics, as `AnyKey` is meant to represent a special case of keyed services rather than a specific registration.

Calling `GetKeyedServices()` with `KeyedService.AnyKey` returned all registrations for `AnyKey`. This behavior was also inconsistent with the intended semantics, as `AnyKey` is not meant to enumerate all keyed services.

## New behavior

Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an <xref:System.InvalidOperationException>. This ensures that `AnyKey` can't be used to resolve a single service, as it's intended to represent a special case rather than a specific key.

```csharp
var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey);
// Throws InvalidOperationException: "Cannot resolve a single service using AnyKey."
```

Additionally, calling `GetKeyedServices()` with `KeyedService.AnyKey` no longer returns registrations for `AnyKey`. Instead, it adheres to the updated semantics where `AnyKey` is treated as a special case and doesn't enumerate services.

```csharp
var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey);
// Returns an empty collection.
```

## Type of breaking change

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).

## Recommended action

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

For `GetKeyedService(KeyedService.AnyKey)`, replace calls to `GetKeyedService()` with `KeyedService.AnyKey` with specific keys or alternative logic to handle service resolution.

## Affected APIs

- <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetKeyedService(System.IServiceProvider,System.Type,System.Object)?displayProperty=fullName>
- <xref:Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetKeyedServices(System.IServiceProvider,System.Type,System.Object)?displayProperty=fullName>
2 changes: 2 additions & 0 deletions docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ items:
items:
- name: BackgroundService runs all of ExecuteAsync as a Task
href: extensions/10.0/backgroundservice-executeasync-task.md
- name: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey
href: extensions/10.0/getkeyedservice-anykey.md
- name: Message no longer duplicated in Console log output
href: extensions/10.0/console-json-logging-duplicate-messages.md
- name: Null values preserved in configuration
Expand Down
Loading