Skip to content

Possible improvements to support .NET 10 AnyKey behavior #1471

@tillig

Description

@tillig

Problem Statement

At the moment, the entirety of the support for adapting Microsoft.Extensions.DependencyInjection registrations into Autofac registrations is in the Autofac.Extensions.DependencyInjection repository. Until .NET 10, we were able to keep these things separate because all of the extensions could be done using the current public API for Autofac.

As of .NET 10 there is more explicit desired behavior for the AnyKey construct (where you can register a keyed service to respond to any key).

Converted into Autofac language, it's like

builder.RegisterType<T>().Keyed("*")

and that has special meaning. Sometimes it shows up in resolved results, sometimes it doesn't. Based on the table in the unit tests...

Query Keyed? Unkeyed? AnyKey? null key?
GetServices(Type) no yes no yes
GetService(Type) no yes no yes
GetKeyedServices(null) no yes no yes
GetKeyedService(null) no yes no yes
GetKeyedServices(AnyKey) yes no no no
GetKeyedService(AnyKey) throw throw throw throw
GetKeyedServices(key) yes no no no
GetKeyedService(key) yes no yes no
  • A null key is the same as unkeyed. This allows the KeyServices APIs to support both keyed and unkeyed.
  • AnyKey is a special case of Keyed.
  • AnyKey registrations are not returned with GetKeyedServices(AnyKey) and GetKeyedService(AnyKey) always throws.
  • For IEnumerable, the ordering of the results are in registration order.
  • For a singleton resolve, the last match wins.

The problem that's being encountered in trying to make it work in an extension pattern is that if you register something like this...

builder.RegisterType<T>().Keyed("*")

then when you resolve a collection of services, sometimes it comes back in the collection and sometimes it doesn't. For example, if you do container.ResolveKeyed<IEnumerable<T>>("key") then the AnyKey service does not come back in the collection, but if you resolve a single item container.ResolveKeyed<T>("key") then the AnyKey service does come back.

At the moment, in Autofac core, we have some shortcomings:

  1. A component can be registered with multiple services, but a keyed service can only respond to a single key. Supporting the concept of AnyKey requires something more dynamic than that.
  2. Whether or not a component is included in a collection is controlled at the component level rather than the service level so you couldn't say "if responding to a keyed service collection request and this was registered with AnyKey, don't include it."

We need to enhance the internals of Autofac somehow to more natively support AnyKey.

Desired Solution

Ideas include...

  • A native concept of AnyKey itself, possibly with a derived version of KeyedService or a well-known key.
  • A more dynamic predicate-based service so a service could match any number of criteria.
  • Ability to specify "participates in collections" at the service level in addition to the registration level.

This isn't prescriptive, but the idea is that the adapter for AnyKey would be able to use more native Autofac constructs instead of trying to mask over things entirely with AEDI.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions