From 1ccce8ea180907aadf315ea2b185bad4bbc0f7c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:36:11 +0000 Subject: [PATCH 1/4] Initial plan From cb5052669dab5755346a9744862b7e63e432b2de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:42:12 +0000 Subject: [PATCH 2/4] Add breaking change documentation for GetKeyedService and GetKeyedServices with AnyKey Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- docs/core/compatibility/10.0.md | 1 + .../extensions/10.0/getkeyedservice-anykey.md | 83 +++++++++++++++++++ docs/core/compatibility/toc.yml | 2 + 3 files changed, 86 insertions(+) create mode 100644 docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md diff --git a/docs/core/compatibility/10.0.md b/docs/core/compatibility/10.0.md index 385de81807dd7..994060f75659e 100644 --- a/docs/core/compatibility/10.0.md +++ b/docs/core/compatibility/10.0.md @@ -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 | diff --git a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md new file mode 100644 index 0000000000000..5ab909b88e22b --- /dev/null +++ b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md @@ -0,0 +1,83 @@ +--- +title: "Breaking change: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey" +description: "Learn about the breaking change in .NET 10 Preview 3 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 `GetKeyedService()` and `GetKeyedServices()` methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the `KeyedService.AnyKey` registration. Specifically, `GetKeyedService()` now throws an exception when you attempt to resolve a single service using `KeyedService.AnyKey` as the lookup key, and `GetKeyedServices()` no longer returns `AnyKey` registrations when queried with `KeyedService.AnyKey`. + +## Version introduced + +.NET 10 Preview 3 + +## Previous behavior + +Previously, calling `GetKeyedService()` with `KeyedService.AnyKey` would return 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. + +Additionally, calling `GetKeyedServices()` with `KeyedService.AnyKey` would return 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 exception. 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 + +Developers using `GetKeyedService()` or `GetKeyedServices()` with `KeyedService.AnyKey` should review their 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. + +**Before**: + +```csharp +var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey); +``` + +**After**: + +```csharp +// Replace AnyKey with a specific key or implement custom logic for service resolution. +var service = serviceProvider.GetKeyedService(typeof(IMyService), specificKey); +``` + +For `GetKeyedServices(KeyedService.AnyKey)`, update code to explicitly enumerate or query services by specific keys instead of relying on `AnyKey`. + +**Before**: + +```csharp +var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey); +``` + +**After**: + +```csharp +// Replace AnyKey with specific keys or implement custom logic for service enumeration. +var services = serviceProvider.GetKeyedServices(typeof(IMyService), specificKey); +``` + +## Affected APIs + +- +- diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index c06b689bde9a1..98e7aa1f57fe9 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -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 From 904c47cc2b935c93b65ffab2cd44af6eb973f552 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:02:53 -0800 Subject: [PATCH 3/4] Apply suggestions from code review --- .../extensions/10.0/getkeyedservice-anykey.md | 42 ++++--------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md index 5ab909b88e22b..0eec9e00936be 100644 --- a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md +++ b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md @@ -1,27 +1,27 @@ --- title: "Breaking change: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey" -description: "Learn about the breaking change in .NET 10 Preview 3 where GetKeyedService() and GetKeyedServices() behavior changed when using KeyedService.AnyKey as the lookup key." +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 `GetKeyedService()` and `GetKeyedServices()` methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the `KeyedService.AnyKey` registration. Specifically, `GetKeyedService()` now throws an exception when you attempt to resolve a single service using `KeyedService.AnyKey` as the lookup key, and `GetKeyedServices()` no longer returns `AnyKey` registrations when queried with `KeyedService.AnyKey`. +The behavior of the and methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the 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 Preview 3 +.NET 10 ## Previous behavior -Previously, calling `GetKeyedService()` with `KeyedService.AnyKey` would return 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. +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. -Additionally, calling `GetKeyedServices()` with `KeyedService.AnyKey` would return all registrations for `AnyKey`. This behavior was also inconsistent with the intended semantics, as `AnyKey` is not meant to enumerate all keyed services. +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 exception. 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. +Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an . 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); @@ -45,38 +45,10 @@ The previous behavior of `GetKeyedService()` and `GetKeyedServices()` with `Keye ## Recommended action -Developers using `GetKeyedService()` or `GetKeyedServices()` with `KeyedService.AnyKey` should review their code and update it to use specific keys instead of `AnyKey`. +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. -**Before**: - -```csharp -var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey); -``` - -**After**: - -```csharp -// Replace AnyKey with a specific key or implement custom logic for service resolution. -var service = serviceProvider.GetKeyedService(typeof(IMyService), specificKey); -``` - -For `GetKeyedServices(KeyedService.AnyKey)`, update code to explicitly enumerate or query services by specific keys instead of relying on `AnyKey`. - -**Before**: - -```csharp -var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey); -``` - -**After**: - -```csharp -// Replace AnyKey with specific keys or implement custom logic for service enumeration. -var services = serviceProvider.GetKeyedServices(typeof(IMyService), specificKey); -``` - ## Affected APIs - From 7b987d3d2c81340ed2acd4f212226b3605b4dcb0 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:27:08 -0800 Subject: [PATCH 4/4] Fix xrefs --- .../extensions/10.0/getkeyedservice-anykey.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md index 0eec9e00936be..835a5fcb1ba81 100644 --- a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md +++ b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md @@ -7,7 +7,7 @@ ai-usage: ai-assisted # Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey -The behavior of the and methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the 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 and methods in the `Microsoft.Extensions.DependencyInjection` library was updated to address inconsistencies in handling the 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 @@ -49,7 +49,9 @@ If you use `GetKeyedService()` or `GetKeyedServices()` with `KeyedService.AnyKey For `GetKeyedService(KeyedService.AnyKey)`, replace calls to `GetKeyedService()` with `KeyedService.AnyKey` with specific keys or alternative logic to handle service resolution. +For `GetKeyedServices(KeyedService.AnyKey)`, replace calls to `GetKeyedServices()` with `KeyedService.AnyKey` with calls that use specific keys, or update your logic to enumerate only the services you intend to retrieve. + ## Affected APIs -- -- +- +-