Autofac has the same feature, but apart from using attributes within the classed, it is also possible to instead specify which key to resolve for a particular class when registering that class. That way, it becomes up to the application architect wiring up the dependency injection to decide what keyed dependencies are meant for which classes, while the classes themselves remain ignorant of this. I usually prefer this approach, instead of having the information leak into the classes, although I like this 'hack' presented here of specifying the class type itself as key.
I needed this _today_. Thank you! If you're curious, my use case is an ETL engine with classes for inputs (like input files but could be anything that results in IEnumerable of data to be provessed) with functionality that includes archiving the source data for audit and troubleshooting needs when it pulls the data, and another set of classes with a `ProcessAndUpload` method that takes the IEnumerable of input data and does stuff. I had separated duties like this so that each input can be read only once and distributed to several different destination handlers that each input class receives via dependency injection. Today, the destination classes are provided via DI, each destination class hardcoded into the input classes that use them. When I switch my code tomorrow, thanks to you, each input class won't need to hardcode the destination handlers by name, the input classes can receive an enumerable of destination handlers through dependency injection of services keyed per input class.
Finally the day has come where I learned nothing from one of your videos! It's a great introduction to Keyed Services and would recommend this video to anyone interested in the topic!
@@zoran-horvat I for example was not aware of this. Additional benefit is that this will work in .NET 8 while Enum approach as you mentioned was fix recently in .NET 9. Great content!
Good material, thanks. Could you discuss shortly the obvious alternative, I mean using a separate interface per usage so for example `IRecommendationBookFormatter', 'IListItemBookFormatter` etc. Details of the names does not matter, just an example. Wouldn't it be more natural to segregate interfaces instead of unifying them just to throw additional layer of abstraction on top of it?
@@krajekdev That doesn't resolve the underlying problem that the consuming service now knows which kind of other service it requires. You will observe that the endpoint would possess the same knowledge as it did when it used the attribute to specify the specific service it needs. The keyed services only make the registration somewhat shorter.
@zoran-horvat It knows what service it requires, yes, but with a keyed service it only hints to what it wants, but won’t say it clearly. It’s inelegant.
The problem is it is asking for an IService, but it doesn’t really mean it. It actually wants an ISpecializedService, so while the two services have the same shape, they are not truly interchangeable. I would solve it with a subtype.
I’m not crazy about keyed services. It asks the consumer to define what to expect, which seems to lessen the value of the injection, and is akin to a service locator. At that point, maybe just let the consumer decide on the implementation it needs?
You cannot let the consumer decide if the decision depends on information the consumer does not possess. For example, consider deployment-dependent assignment of services.
To set up a unit test, you would need to register the service in your IOC container. With this approach, i don’t think that’s necessary. Makes the test cleaner.
I think I missed something at the end of the video when page model received a sequence of formatter services. How can page choose the proper one from this sequence? If the answer is selecting only one service by its specific type then looks like we get leaking information to the page model class again because it should know what concrete type of formatter it's needed. So, this my assumption is incorrect but I still don't understand what is the correct way to work with IEnumerable inside the consumer class. If Zoran could show this refactoring in the video for the page model class it would be clearly to understand.
@@ДмитрийКондратенко-б5ь The using class does not make a choice. This concept is meant for the classes that need to call multiple services to complete their work, so you can configure multiple instances of that service interface.
@@zoran-horvat, oh, I got it. This approach only for consumer that needs to have many formatters and apply them as a chain. Thanks for explanation. I didn't hear this point in the video so it confused me.
@@zoran-horvat To format thing differently, class gets IEnumeable of formatiing services, then must have a way to select one or another, so must select first, second, etc. or have some konwledge about specific formater types or they have some abstract mechanism to query who they are or some attributes of them.
@AK-vx4dy You are missing the point. The class doesn't have any special knowledge of how any of the services differs from the others. It knows nothing about them past what their interface says. Don't you see that in the demo, too? The endpoint uses all services uniformly.
@@zoran-horvat I'm talking about part when you show possiblity of taking IEnumerable and injecting more that one formater without a key, model must have some assumption that for example for left column uses first element and for right second element from this IEnumerable, so order of declaration decides or model have some knowledge about formaters.
Good content. You might know that using the class type as a key restricts DI registration to one class-it's confusing from the consumption standpoint. Registering multiple classes with a single key is fine since the consumer doesn't have to know the types in the Enumerable ;)
But now your domain depends on DI framework that is used and whether it supports that FromKeyedServiceAttribute or not. It is similar pain how EF mapping via attributes used to be and nowadays everyone is using code first mapping.
How is the domain dependent on the keyedservices here exactly? The interface registered in DI does not know about the keyed services attribute so you must mean something different is dependent on the keyedservices attribute
@@andreybiryulin7207 It is the endpoint/controller that uses the FromKeyedServices attribute, and it belongs to the outermost services layer, which is already tightly bound to ASP.NET Core in all kinds of ways. Nothing below the services layer, and certainly not the domain, knows anything about how the injection is performed. Therefore, the domain model does not depend on the DI framework.
Autofac has the same feature, but apart from using attributes within the classed, it is also possible to instead specify which key to resolve for a particular class when registering that class. That way, it becomes up to the application architect wiring up the dependency injection to decide what keyed dependencies are meant for which classes, while the classes themselves remain ignorant of this. I usually prefer this approach, instead of having the information leak into the classes, although I like this 'hack' presented here of specifying the class type itself as key.
@@jhdk356 I also hoped ASP.NET Core DI would take that route, but it seems they didn't choose it.
I needed this _today_. Thank you!
If you're curious, my use case is an ETL engine with classes for inputs (like input files but could be anything that results in IEnumerable of data to be provessed) with functionality that includes archiving the source data for audit and troubleshooting needs when it pulls the data, and another set of classes with a `ProcessAndUpload` method that takes the IEnumerable of input data and does stuff. I had separated duties like this so that each input can be read only once and distributed to several different destination handlers that each input class receives via dependency injection. Today, the destination classes are provided via DI, each destination class hardcoded into the input classes that use them. When I switch my code tomorrow, thanks to you, each input class won't need to hardcode the destination handlers by name, the input classes can receive an enumerable of destination handlers through dependency injection of services keyed per input class.
Finally the day has come where I learned nothing from one of your videos!
It's a great introduction to Keyed Services and would recommend this video to anyone interested in the topic!
@@W1ese1 Nice! But did you know you can use typeof operator to create the service key?
@@zoran-horvat I for example was not aware of this. Additional benefit is that this will work in .NET 8 while Enum approach as you mentioned was fix recently in .NET 9. Great content!
Good material, thanks. Could you discuss shortly the obvious alternative, I mean using a separate interface per usage so for example `IRecommendationBookFormatter', 'IListItemBookFormatter` etc. Details of the names does not matter, just an example. Wouldn't it be more natural to segregate interfaces instead of unifying them just to throw additional layer of abstraction on top of it?
@@krajekdev That doesn't resolve the underlying problem that the consuming service now knows which kind of other service it requires. You will observe that the endpoint would possess the same knowledge as it did when it used the attribute to specify the specific service it needs. The keyed services only make the registration somewhat shorter.
@zoran-horvat It knows what service it requires, yes, but with a keyed service it only hints to what it wants, but won’t say it clearly. It’s inelegant.
@@KristianVidemarkParkov How would you configure that in a config file? What makes fixing the services at compile time elegant?
The problem is it is asking for an IService, but it doesn’t really mean it. It actually wants an ISpecializedService, so while the two services have the same shape, they are not truly interchangeable. I would solve it with a subtype.
@@KristianVidemarkParkov How do you implement concrete classes, then?
Nice tip. Thanks
Great Lesson!
Great video!
I’m not crazy about keyed services. It asks the consumer to define what to expect, which seems to lessen the value of the injection, and is akin to a service locator. At that point, maybe just let the consumer decide on the implementation it needs?
You cannot let the consumer decide if the decision depends on information the consumer does not possess. For example, consider deployment-dependent assignment of services.
Did I miss something, I usually use service locator pattern, what's the trade offs for this keyed service vs locator pattern?
To set up a unit test, you would need to register the service in your IOC container. With this approach, i don’t think that’s necessary. Makes the test cleaner.
I think I missed something at the end of the video when page model received a sequence of formatter services. How can page choose the proper one from this sequence? If the answer is selecting only one service by its specific type then looks like we get leaking information to the page model class again because it should know what concrete type of formatter it's needed. So, this my assumption is incorrect but I still don't understand what is the correct way to work with IEnumerable inside the consumer class. If Zoran could show this refactoring in the video for the page model class it would be clearly to understand.
@@ДмитрийКондратенко-б5ь The using class does not make a choice. This concept is meant for the classes that need to call multiple services to complete their work, so you can configure multiple instances of that service interface.
@@zoran-horvat, oh, I got it. This approach only for consumer that needs to have many formatters and apply them as a chain. Thanks for explanation. I didn't hear this point in the video so it confused me.
@12:00 Great material, but depending on order of occurence declaration in code i can not praise.
@@AK-vx4dy But the class doesn't depend on the order. It executes the actions in the order given, which it doesn't know.
@@zoran-horvat To format thing differently, class gets IEnumeable of formatiing services, then must have a way to select one or another, so must select first, second, etc. or have some konwledge about specific formater types or they have some abstract mechanism to query who they are or some attributes of them.
@AK-vx4dy You are missing the point. The class doesn't have any special knowledge of how any of the services differs from the others. It knows nothing about them past what their interface says.
Don't you see that in the demo, too? The endpoint uses all services uniformly.
@@zoran-horvat I'm talking about part when you show possiblity of taking IEnumerable and injecting more that one formater without a key, model must have some assumption that for example for left column uses first element and for right second element from this IEnumerable, so order of declaration decides or model have some knowledge about formaters.
Good content. You might know that using the class type as a key restricts DI registration to one class-it's confusing from the consumption standpoint. Registering multiple classes with a single key is fine since the consumer doesn't have to know the types in the Enumerable ;)
@@marcobaccaro Yes, that is what I wanted to demonstrate. You can group services by features, and then configure a different choice for one page only.
But now your domain depends on DI framework that is used and whether it supports that FromKeyedServiceAttribute or not. It is similar pain how EF mapping via attributes used to be and nowadays everyone is using code first mapping.
How is the domain dependent on the keyedservices here exactly? The interface registered in DI does not know about the keyed services attribute so you must mean something different is dependent on the keyedservices attribute
@ your domain ctor code uses FromKeyedServiceAttribute and relies that DI framework supports it to understand what to inject
@@andreybiryulin7207 It is the endpoint/controller that uses the FromKeyedServices attribute, and it belongs to the outermost services layer, which is already tightly bound to ASP.NET Core in all kinds of ways. Nothing below the services layer, and certainly not the domain, knows anything about how the injection is performed. Therefore, the domain model does not depend on the DI framework.