Open luizfbicalho opened 1 year ago
Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection See info in area-owners.md if you want to be subscribed.
I would also like an alternative to the question
@benjaminpetit any thoughts on the keyed-service ask for make the Service Type and Key unique along with validation?
But to make the Editor for the steps I need to list all of the Keys that are registered in the ServiceProvider
For this scenario, is it possible to enumerate the ServiceCollection instead which can return the ServiceDescriptors?
Also since there can be several implementations of IServiceProvider\IKeyedServiceProvider, that has to be accounted for in any extension methods -- i.e. they need to be implemented in terms of types exposed in the Microsoft.Extensions.DependencyInjection.Abstractions
assembly, not the types in Microsoft.Extensions.DependencyInjection
for example. This likely means we'd have to add new interfaces as well to expose the keys.
But to make the Editor for the steps I need to list all of the Keys that are registered in the ServiceProvider
For this scenario, is it possible to enumerate the ServiceCollection instead which can return the ServiceDescriptors?
Also since there can be several implementations of IServiceProvider\IKeyedServiceProvider, that has to be accounted for in any extension methods -- i.e. they need to be implemented in terms of types exposed in the
Microsoft.Extensions.DependencyInjection.Abstractions
assembly, not the types inMicrosoft.Extensions.DependencyInjection
for example. This likely means we'd have to add new interfaces as well to expose the keys.
How can i get the service colector from the service provider?
@benjaminpetit any thoughts on the keyed-service ask for make the Service Type and Key unique along with validation?
If we add this, then we should add the same method for non-keyed DI in my opinion.
I think for this and the listing of all registered key, you could implement your own IServiceProviderFactory
that will validate that you don't have two or more services registered with the same key, and build a list of available keys. You can then insert this list as a new service to the IServiceCollection
before passing it to the "real" IServiceProviderFactory` implementation.
Does that make sense?
If we add this, then we should add the same method for non-keyed DI in my opinion.
Would it be useful to add something like this
serviceCollection.AddScoped<IBar, Bar>().AsUnique();
Or add some more validation with a func
serviceCollection.AddScoped<IBar, Bar>().WithValidation(collection=>VerifySomething(collection));
Or even mark some types as required, this could be even used with GetRequiredService to validate.
serviceCollection.Mark<IBar>().AsRequired();
ServiceProviderOptions.ValidateOnBuild
should already allow you to check that your services are instantiable.
Otherwise, I think everything you want to do is doable with some kind of decorator around IServiceProviderFactory
Can you provide any example like this? How to validate with the factory
I'm interested in the @steveharter idea of get the service collection from the service provider
Here is what I have in mind: https://gist.github.com/benjaminpetit/ba60099a99f6cb315074c373b06d9d32
I implemented a custom service provider factory in CustomServiceProviderFactory
. I tell the HostBuilder
to use it instead of the default one.
AsUnique()
extension change the service type of the descriptor to a type that implement IServiceMarker
. When the host will build the service provider, it will call CustomServiceProviderFactory.CreateServiceProvider
that can iterate through all the service descriptors. When it sees a service that implement IServiceMarker
, it will call the method Validate
, then "unwrap" the descriptor to use the correct service type.
When iterating though the descriptors, you could build a dictionary of keyed services too, for example.
With the same logic you could implement anything you wanted; I think.
EDIT: I think we could do much simpler without this generic descriptor wrapping/unwrapping thing.
Here is an example on how to generate a dictionary of all keyed services: https://gist.github.com/benjaminpetit/468741882f1ad6e4ec8dad761103a87d
Thanks a lot @benjaminpetit , I'll try to transform this in a usefull library
Maybe some of it can go into this future projetc
I just hit this hard as well. I needed to be able to get all of the instances for a particular service type, regardless of key (I didn't actually need the keys), and I thought GetKeyedServices(KeyedService.AnyKey)
would work, but it doesn't, as the key matching doesn't factor AnyKey in for the key being searched. This is an unfortunate gap.
I was going to use keyed services for the first time, and also hit this limitation. I can work around it, but this gap should be closed IMO.
IMHO, this is more of a bug. Documentation for KeyedService.AnyKey
specifically says:
Represents a key that matches any key.
So GetKeyedServices<T>(KeyedService.AnyKey)
should most definitely return an enumeration of all T
implementations.
Similar, col.AddKeyedTransient<IService, ServiceA>(KeyedService.AnyKey);
should give an error. If you want to have a fallback, null
should be used.
Hey folks! Just wanted to find out is this was planning on getting fixed at all? There shouldn't need to be hacks to make this work, since KeyedService.AnyKey
exists and should be implemented. Thanks!
There's a discussion going on at #100105 (which is a duplicate of this issue).
There's a discussion going on at #100105 (which is a duplicate of this issue).
I don't think that all of the features here are in the #100105 issue
Yeah, I have a scenario where I need to register services to get pulled specifically for different use cases to load specific types of data, but also need to gather them all (thus the common interface) to initialize them beforehand.
I got excited by the new keyed services as it seemed built for this scenario, but then got stuck in the last step after changing my code in trying to get all the services. I was hoping I could just call GetKeyServices<T>()
to get them all, I didn't even find the docs on KeyedService.AnyKey
, but then that seemed great too (as called out above)... but then that also didn't work. So, I think I'm back to being stuck without hacky workarounds...
Edit: Oh, just saw the KeyedService.AnyKey
was fixed in .NET 9 https://github.com/dotnet/runtime/issues/109016 🎉🎉🎉, hopefully it'll get backported too. (Just a note for anyone here, you have to ensure to update your NuGet package to the latest .NET 9 version too, not just your TFM.)
@michael-hawker
Edit: Oh, just saw the
KeyedService.AnyKey
was fixed in .NET 9 #109016 🎉🎉🎉, hopefully it'll get backported too. (Just a note for anyone here, you have to ensure to update your NuGet package to the latest .NET 9 version too, not just your TFM.)
I don't think you even need to update your TFM. Isn't this a behavior of the library itself, Microsoft.Extensions.DependencyInjection
? Upgrading the library to v9 will be enough even if you are still using .NET 8, since the fix doesn't rely on any #if
checks against a specific TFM.
@julealgon indeed, thanks! It was late at night, so I had bumped the TFM first before realizing it was just a package dependency. 😅 Worked like a charm!
Background and motivation
I have some configurations in my process that I create for each step one service and store it in a EF class
But to make the Editor for the steps I need to list all of the Keys that are registered in the ServiceProvider
One second utility would be to Make the Service Type and Key marked as unique
this way if I add two services I would get a validation exeption
and the third question is to have a method provider.GetAllServices() that return all services from all keys
that could be a keyed dictionary if it would help organize
API Proposal
API Usage
Alternative Designs
I'm open to any alternative that could result in this functionalities
Risks
I can't imagine risks because there are new methods to implement this.