Dependency Injection Is Now Complete In .NET 8!

preview_player
Показать описание


Hello, everybody, I'm Nick, and in this video, I will show you a brand new feature that was added in .NET 8 for Dependency Injection that many people, including myself, were missing.

Don't forget to comment, like and subscribe :)

Social Media:

#csharp #dotnet
Рекомендации по теме
Комментарии
Автор

The use case that's actually useful for this is providing multiple versions of your API. You decorate your services worth attributes that specify versions, you inject specific version based on the requested API version that was invoked.

veljkozrnic
Автор

Regarding naming the keys for injection, they should be named for their purpose, not for their implementation. Instead of "openweathermap" and "weatherapi" they should be "main" and "backup". That is the point of injecting interfaces as in the future you may decide to prefer weatherapi as you main service for example.

RandallEike
Автор

This feels suspiciously like the service locator antipattern. Also, it breaks separation of concerns somewhat, unless your concern is the fallback mechanism specifically. I'd create an IWeatherService implementation, FallbackWeatherService, or something, which would take two (or a list of) IWeatherServices and register that in the DI container.

mrwensveen
Автор

if you hardcode a key when requiring a service through di then what is even the point? why not inject the service directly without an interface?

ddruganov
Автор

Am I missing something as to why a "keyed" service with an alias is preferable to just explicitly registering the service by its own type when you want to request a *specific* instance? If not that, then as others mention, a factory that handles resolving the service for you is the smartest and most reliable solution. I dont really get why someone would even need something like this.

MrEnvisioner
Автор

This demonstrates how to get both the main and backup services for retrieving weather. But what I thought you were going to show is how to gracefully degrade to the backup when the main service is down; that is the more interesting problem here.

RandallEike
Автор

I would add one use case I see frequently that this solves more elegantly. We use a lot of Dapper here, and so our services regularly need an IDbConnection, but sometimes to two differrent databases (different connection strings). Because a service or repository class needs an IDbConnection, currently, we have to do a lot of extra work at the DI registration to figure out which IDbConnection we need for the service we're calling. With this, we can declare at the service's constructor which IDbConnection we need, or expect. Or at the DI registration, get the keyed service before calling the service's constructor.

KnightSwordAG
Автор

While interesting, I can't think of a use case for this.

In a scenario like the one you've described, I feel a better solution would be to create a singleton wrapper class that takes in IEnumerable<IWeatherService> and cycles to the next service whenever a 500 is returned(or any other "cycle to next" conditions). That would allow setting up a variable amount of backup services without requiring the usage of keys.

a-minus-b-equals-a
Автор

Used to use Autofac back in the day, and while I understand adding this for completion, it's not been a feature I've missed.
Having to annotate the dependency requires knowing the key at design time, which feels (to me) like it violates the idea of "inversion of control".
When I've had a use case to use one of N implementations in the past, I've just registered a good old factory delegate that would hand me the registered service I needed based on the function parameter I passed in.
Having said that, it does allow a neater method for creating decorators instead of using something like Scrutor or rolling your own decorator extensions.

petewarner
Автор

Another work-around is to inject a factory class that can resolve your dependency based on whatever key as well. This new approach would be a little cleaner though.

kaizer-
Автор

Back in the day Ninject has different approach for that.
It was something like => *filter*); //or
And that was better, because you still manage your dependencies in startup, and not hardcoding some "alias" in your dependent class.
As other comments mention, approach with Keyed/Named services is not any better than just binding different services to different (inherited) interfaces - dependent classes should know that they want, they should known name/key/alias.

nqlnpsx
Автор

I genuinely hoped they will never bring it to default DI. It is quite an antipattern that breaks the whole point of using DI, which is explicitly about _not_ specifying desired implementation. Loose coupling goes straight out of the window with the "keyed DI" approach.

Nworthholf
Автор

I would really love for custom resolve methods. For example Would prefer to have a class A { A(ILogger logger){}} instead of class A { A(ILogger<A> logger){}} and have it resolve depending on what type it targets

EikeSchwass
Автор

I really don't like the idea of having to modify some implementation in order for the dependency injection to work as expected.
If I write a service to call some API, read from a db or do literally anything else, it shouldn't concern itself with how it is treated by some other part of the code.
It should simply demand its dependencies and expose its functionality. And I don't need a DI container to implemenent any service itself.

In this example I think I'd much rather have a third IWeatherService implementation which takes a number of implementations and contains the logic of when to choose which and fowards the interface calls and then just register that one.
You could alternatively have a WeatherServiceResolver service that does the resolving and returns the chosen implementation. Although that would have the same issue as the keyed stuff. Anything that needs a weatherService, needs a weatherservice and shouldn't concern itself with the fact that I might wanna dynamically choose between different implementations, or that I use a DI container.

mkwpaul
Автор

sweating from 20 degrees
i would like to invite you to my house in Egypt 😅 20 is when i wear my winter clothes

AhmedMohammed
Автор

A factory is the best of all worlds, in order to create any different type of instances. Thanks for the great presentation!

pavfrang
Автор

Can you then inject Dictionary<object, Foo> and pick your service out in the constructor? The attribute looks clean and all, but it looks like most of the benefit is lost if I have to hardcode the key

wojciechwilimowski
Автор

Thanks for mentioning Scrutor! For needs like caching, retries, etc., I've always manually wired up decorators, and it looks like Scrutor should make that much cleaner.

JerryFederspiel
Автор

Not a fan of this feature - I don't like attributes and I reckon the enumerable route with a CanProcess<T>() on the interface is cleaner and also easy to test. Guess there's situations it's useful like heavy service constructors, but I'd argue there's a design problem to consider?

Always good to know though - thanks for sharing 🙂

ryancookparagliding
Автор

I do like the idea, but I personally wouldn't use it. Mainly because it tightly couples your code to the DI framework itself (in this case,

You can achieve pretty much the same thing, albeit not as elegantly, using the factory pattern.

jamesmussett