Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 19, 2025

Summary

Documents breaking behavioral changes in GetKeyedService() and GetKeyedServices() when using KeyedService.AnyKey in .NET 10 Preview 3.

Changes

  • Added breaking change article: docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md

    • GetKeyedService() now throws InvalidOperationException when called with KeyedService.AnyKey
    • GetKeyedServices() returns empty collection instead of AnyKey registrations
    • Includes migration guidance with before/after code examples
  • Updated navigation: Added entries to toc.yml and 10.0.md index under Extensions section

Example

// Before: Would return a service or registrations
var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey);
var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey);

// After: Throws exception and returns empty collection
var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey);
// Throws InvalidOperationException

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

Related: dotnet/runtime#113137

Original prompt

This section details on the original issue you should resolve

<issue_title>[Breaking change]: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey</issue_title>
<issue_description>### Description

The behavior of GetKeyedService() and GetKeyedServices() methods in the Microsoft.Extensions.DependencyInjection library has been updated to address inconsistencies in handling the AnyKey registration. Specifically:

  1. Behavioral Change: The GetKeyedService() method now throws an exception when attempting to resolve a single service using KeyedService.AnyKey as the lookup key.
  2. Behavioral Change: The GetKeyedServices() method no longer returns AnyKey registrations when queried with KeyedService.AnyKey.

These changes were introduced to improve consistency in the behavior of keyed service resolution and align with the intended semantics of KeyedService.AnyKey.

Version

.NET 10 Preview 3

Previous behavior

  1. GetKeyedService(KeyedService.AnyKey):

    • 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.
  2. GetKeyedServices(KeyedService.AnyKey):

    • Previously, calling GetKeyedServices() with KeyedService.AnyKey would return all registrations for AnyKey. This behavior was inconsistent with the intended semantics, as AnyKey is not meant to enumerate all keyed services.

New behavior

  1. GetKeyedService(KeyedService.AnyKey):

    • Calling GetKeyedService() with KeyedService.AnyKey now throws an exception. This ensures that AnyKey cannot be used to resolve a single service, as it is intended to represent a special case rather than a specific key.

    Example:

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

    • 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 does not enumerate services.

    Example:

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

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or targeting the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at runtime.

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:

  1. Ensure that AnyKey is treated as a special case and cannot be used to resolve a single service.
  2. 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.

Recommended action

Developers using GetKeyedService() or GetKeyedServices() with KeyedService.AnyKey should review their code and update it as follows:

  1. For GetKeyedService(KeyedService.AnyKey):

    • Replace calls to GetKeyedService() with KeyedService.AnyKey with specific keys or alternative logic to handle service resolution.

    Before:

    var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey);

    After:

    // Replace AnyKey with a specific key or implement custom logic for service resolution.
    var service = serviceProvider.GetKeyedService(typeof(IMyService), specificKey);
  2. For GetKeyedServices(KeyedService.AnyKey):

    • Update code to explicitly enumerate or query services by specific keys instead of relying on AnyKey.

    Before:

    var services = serviceProvider.GetKeyedServices(typeof(IMyService), KeyedService.AnyKey);

    After:

    // Replace AnyKey with specific keys or implement custom logic for service enumeration.
    var services = serviceProvider.GetKeyedServices(typeof(IMyService), specificKey);

Feature area

  • Extensions

Affected APIs

  • Microsoft.Extensions.DependencyInjection.ServiceProvider.GetKeyedService(Type, object)
  • `Microsoft.Extensions.DependencyInjection.ServiceProvider.GetKey...

Custom agent used: breakingchange-creator
Agent that specializes in creating breaking change articles


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


Internal previews

📄 File 🔗 Preview link
docs/core/compatibility/10.0.md Breaking changes in .NET 10
docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey
docs/core/compatibility/toc.yml docs/core/compatibility/toc

…vices with AnyKey

Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issues with GetKeyedService and GetKeyedServices for AnyKey Document breaking change for GetKeyedService and GetKeyedServices with AnyKey in .NET 10 Preview 3 Nov 19, 2025
Copilot AI requested a review from gewarren November 19, 2025 21:45
Copilot finished work on behalf of gewarren November 19, 2025 21:45
@gewarren gewarren marked this pull request as ready for review November 20, 2025 01:03
@gewarren gewarren requested a review from a team as a code owner November 20, 2025 01:03
Copilot AI review requested due to automatic review settings November 20, 2025 01:03
@gewarren gewarren enabled auto-merge (squash) November 20, 2025 01:04
Copilot finished reviewing on behalf of gewarren November 20, 2025 01:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR documents breaking behavioral changes in GetKeyedService() and GetKeyedServices() methods when using KeyedService.AnyKey in .NET 10 Preview 3. The changes fix inconsistencies in how these methods handled the special AnyKey registration.

Key changes:

  • New breaking change article documenting the exception thrown by GetKeyedService() and the empty collection returned by GetKeyedServices() when using AnyKey.
  • Updated table of contents and .NET 10 index to include the new breaking change entry.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
docs/core/compatibility/toc.yml Added navigation entry for the new breaking change article
docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md New breaking change documentation with behavior details, code examples, and migration guidance
docs/core/compatibility/10.0.md Added table entry for the breaking change in the Extensions section

Copy link
Member

@BillWagner BillWagner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM @gewarren

Let's :shipit:

@gewarren gewarren merged commit ccd47f0 into main Nov 20, 2025
11 checks passed
@gewarren gewarren deleted the copilot/fix-getkeyedservice-anykey-issues branch November 20, 2025 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Breaking change]: Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey

3 participants