-
-
Notifications
You must be signed in to change notification settings - Fork 846
Description
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:
- 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.
- 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.