Це відео не доступне.
Перепрошуємо.

Dependency Injection Is Now Complete In .NET 8!

Поділитися
Вставка
  • Опубліковано 13 сер 2023
  • Use code ARCH15 and get 15% off the brand new Solution Architecture course on Dometrain: dometrain.com/...
    Become a Patreon and get source code access: / nickchapsas
    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.
    Workshops: bit.ly/nickwor...
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasG...
    Follow me on Twitter: bit.ly/ChapsasT...
    Connect on LinkedIn: bit.ly/ChapsasL...
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

КОМЕНТАРІ • 268

  • @veljkozrnic3897
    @veljkozrnic3897 Рік тому +38

    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.

    • @Chris-ud8bi
      @Chris-ud8bi 11 місяців тому +2

      Underrated comment

  • @RandallEike
    @RandallEike Рік тому +79

    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.

    • @Psykorr
      @Psykorr Рік тому +2

      Ok not really the piont of the video I guess but maybe it is a correct statement any way I think

    • @robslaney3729
      @robslaney3729 Рік тому +2

      You also now have to know which one you want at compile time. Can't switch to backup without recompiling.

    • @Rider0fBuffalo
      @Rider0fBuffalo Рік тому +5

      This makes sense to me, because using names of service implementation itself is pointless because you might as well just register, and resolve the service by class instead of by interface.

    • @gregorymeadows3572
      @gregorymeadows3572 Рік тому

      @@robslaney3729 He didn't show it here as its implied, but you would conditionally choose which one to use where based on whatever logic your business needs, feature flags or whatever. (If X request fails, call Y instead)

    • @MikeMcGranahan
      @MikeMcGranahan 11 місяців тому +1

      The author makes this point at the end of the video. Would've been better to demonstrate it in the example code.

  • @mrwensveen
    @mrwensveen Рік тому +57

    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.

    • @robl39
      @robl39 Рік тому +3

      100% agree service locator makes me vomit!

    • @MikeMcGranahan
      @MikeMcGranahan 11 місяців тому +5

      The keys used in the code example do reflect bad practice. But the discussion at the end about "throttled"/"unthrottled", "internal"/"external", "cached"/"uncached" are valid use cases where this feature can be used in a Good™ way.

    • @dirkp.6181
      @dirkp.6181 10 місяців тому +2

      ​@@MikeMcGranahan But still, those are technical concerns and must by no means litter business code.

  • @KnightSwordAG
    @KnightSwordAG Рік тому +17

    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.

    • @Fred-yq3fs
      @Fred-yq3fs Рік тому

      Scary! Your container should not contain any app logic let alone any complex logic. It's not the job of a container, it's typically the job of a factory. Keyed services are simple factories. It's sugar to avoid writing a factory. If you find yourself calculating the key in your callers, then that scattered logic would better live in a factory. If your client passes an enum and it alone decides which implementation to use then fine: use keyed services and write less code.

    • @Nworthholf
      @Nworthholf Рік тому

      In that case connections to different databases should be under their own respective interfaces tho, otherwise you are defying the point of using DI

    • @KnightSwordAG
      @KnightSwordAG 9 місяців тому

      @@Fred-yq3fs you misunderstand. It ultimately becomes a factory of keys to db connections. And it always seemed weird to me to pass the factory as a dependency to a class that only needs the connection.

  • @RandallEike
    @RandallEike Рік тому +8

    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
      @RandallEike Рік тому +2

      @@jkdmyrs yes, I realize you could simply try and fall back there, but that isn't very elegant. If one service is down (or worse, very slow) your API slows down. And perhaps that business logic is not appropriate to house in the controller either. Polly is designed for dealing with failures, but how to use Polly in this situation is not completely trivial.

    • @ChrisWard74
      @ChrisWard74 Місяць тому

      I was hopeing for a demonstration of this too. If the first one is down or rate limited how do we get it to use the second gracefully.

  • @petewarner1077
    @petewarner1077 Рік тому +18

    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.

    • @tkrafael
      @tkrafael Рік тому

      Using a factory might violate service locator anti pattern. And because method/constructor signature doesn't have explicit dependency fire that service (only to factory) it would be harder to test and troubleshoot missing dependencies

    • @petewarner1077
      @petewarner1077 Рік тому +1

      @tkrafael I don't think it violates that because the delegate merely chooses a service implementation based on inputs, like moving a switch statement out of inline to the container. The client code doesn't take a dependency on IServiceProvider, just a Func.

  • @MrEnvisioner
    @MrEnvisioner Рік тому +77

    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.

    • @Stealthy5am
      @Stealthy5am Рік тому +7

      One reason for not registering and resolving by the concrete type is you'll not be able to mock these dependencies in unit tests. Alternatively, you can make a "sub-interface" for each key, but then you end up with marker interfaces, which is not much better.
      Although, I do think this feature is mostly just bloat and the problem can easily be solved by using factories as you mentioned. In my experience, this problem does not come up often enough to warrant bloat in the library, but MS probably has more data to justify their decision.

    • @stevenodlum4563
      @stevenodlum4563 Рік тому +3

      My assumption would be for unit testing. Decorating the interface with the alias just tells dotnet to use a specific implementation when the API is running normally, but you can still manually provide mocked services for unit tests.

    • @justgame5508
      @justgame5508 Рік тому +2

      Less abstraction I guess, this doesn’t require a factory with some CreateClient() logic and a class to go along with that to resolve a service. It’s just the service you want and a name, which has its benefits (less indirection). Then again I think this syntax in the constructor parameters is ugly so I’d personally chose a factory over this

    • @vonn9737
      @vonn9737 Рік тому

      @@Stealthy5am You can register both with their own interfaces which inherit a base interface. IOpenWeatherApi : IWeatherBase and IWeatherApi : IWeatherBase. So that's no problem.

    • @gileee
      @gileee Рік тому +3

      Cause Java Spring has it...

  • @ddruganov
    @ddruganov Рік тому +33

    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?

    • @PublicVoidFoo
      @PublicVoidFoo Рік тому +6

      The main reason would be so that you could still mock your dependency in unit tests

    • @ddruganov
      @ddruganov Рік тому +3

      @@PublicVoidFoo okay yeah makes sense thanks

  • @a-minus-b-equals-a
    @a-minus-b-equals-a Рік тому +13

    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 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.

    • @tkrafael
      @tkrafael Рік тому

      Suppose you have a multi tenant system that checks for user's phone balance.
      Whenever your api is called, you have to call balance api on carrier's api. That api does not have a default contract, to the point done aren't even using http/rest.
      By using keyed service, you are able to resolve a specific implementation from user's carrier.

    • @a-minus-b-equals-a
      @a-minus-b-equals-a Рік тому +4

      @@tkrafael I might be missing something here.
      If, from the same API route, we want to get a different service based on the caller's carrier, how would it work with this system, given that the keys are hardcoded? The only way I can see this working is if we use a proxy wrapper class/dictionary to map to the correct service, rather than specifying a hardcoded key for endpoint.

    • @sebastianxx3687
      @sebastianxx3687 Рік тому

      how about for example as part of strategy pattern ? 1 payment interfaces - 3 different implementations in 3 different services

    • @a-minus-b-equals-a
      @a-minus-b-equals-a Рік тому

      @@sebastianxx3687 If the different implementations need to have a distinct identity, imho it'd make more sense to add an intermediate interface to distinguish between them.
      So, if you have multiple IWeatherService that need to be distinguished from each other(maybe by a proxy factory class which then returns them as generic IWeatherService), instead of [FromKeyedService("openapi")], you'd implement an interface that inherits IWeatherService, so you'd request IOpenAPIWeatherService instead. The end result is about the same, so using keys just feels like a weak-typed alternative.

    • @Nworthholf
      @Nworthholf Рік тому

      @@sebastianxx3687 its not a container's concern to choose strategy. Binding it that way denies you the main purpose of using DI, which is implementation agnosticism

  • @user-nq8ln7ps7x
    @user-nq8ln7ps7x Рік тому +11

    Back in the day Ninject has different approach for that.
    It was something like Bind().To().WhenInjectingInto(x => *filter*); //or WhenInjectingInto()
    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.

    • @dobripopov8466
      @dobripopov8466 Рік тому +1

      Definitely the Ninject approach is better.
      It is container's responsibility to inject the correct implementation of a specific interface. The "consumer" should NOT be aware of what instances have been registered inside the container and to know their keys/names.
      Hopefully it's not too late for Microsoft to redesign their DI framework before the release in November.

  • @kaizer-777
    @kaizer-777 Рік тому +68

    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.

    • @briumphbimbles
      @briumphbimbles Рік тому +18

      I would still prefer a factory over this.

    • @metaltyphoon
      @metaltyphoon Рік тому +6

      Or if you don’t need to mock, just registered a class without interface.

    • @sheveksmath702
      @sheveksmath702 Рік тому +4

      Just curious, but why would you prefer a factory over this?

    • @kaizer-777
      @kaizer-777 Рік тому

      @@sheveksmath702 I wouldn't. I was pointing out another work-around.

    • @JonWoo
      @JonWoo Рік тому +8

      I would also prefer a factory over this. This approach has the controller deciding which service to use.

  • @Nworthholf
    @Nworthholf Рік тому +2

    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.

    • @jelle2819
      @jelle2819 Рік тому +1

      Agree, the cases people use this for tend to be fragile and non intuitive. Even Nick falls for that. I have seen layers of cache, dbcached and remote layers as adapters. And then somebody forgets to use the key. They just get one... but wich... all uses of keyed injection result in a huge nightmare. I'd happily write an analyser that flags this feature as a design error 😂

  • @EikeSchwass
    @EikeSchwass Рік тому +6

    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 logger){}} and have it resolve depending on what type it targets

  • @JerryFederspiel
    @JerryFederspiel Рік тому +4

    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.

    • @jfpinero
      @jfpinero Рік тому +1

      I use scrutor to decorate services that need caching, works really well the [Decorate] attribute.

  • @rogeriobarretto
    @rogeriobarretto Рік тому +10

    When you started talking about backup weather services I was thinking the DI would be able to understand what service is healthy as well and return it accordingly. That said, would be interesting if they could expose a interface to help how to resolve which service in case of multiple ones. I believe we would need a intermediary/wrapper Backup IWeatherService that gets both Weathers to property resolve which one to use.
    That would be an interesting example!

    • @davidbottiau5408
      @davidbottiau5408 Рік тому

      I thought the same too. But at the same time I was thinking "How could that even be possible?". If it was possible, it would be magic. However, giving the IEnumerable of registered interfaces, I suppose we can code our own wrapper that will select the best implementation based on your needs. Like exporting a "Ready" instance property. Not sure if DI is abolutely necessary in this context but It's doable.

    • @paulmdevenney
      @paulmdevenney Рік тому +1

      If they are both singletons then you are actually probably better having an "IsHealthy" ,getting the IEnumerable and chosing the best matching healthy service.

    • @tkrafael
      @tkrafael Рік тому

      For those scenarios, I use scrutor library so I can stack implementation and be able to call as decorated classes

  • @mkwpaul
    @mkwpaul Рік тому +5

    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.

  • @dirkp.6181
    @dirkp.6181 10 місяців тому

    Caution with usage of keyed/named services, because it might introduce code smells. If the services are meant for redundancy, they are necessarily _semantically_ equivalent. Hence it should be transparent for the consumer and the consuming code should not be forced to chose. To implement redundancy is a separate, technical concern. Create some wrapper with polly for instance, but keep the consuming business code clean!
    However, if the named services convey different semantics, they should probably be distinguishable not only by some config name, but rather by an interface.
    Also note, that the naming introduces an dependency, that DI is exactly meant to overcome! "Ask for a fulfilling service and _don't_ care for the implementation!"
    I confess that I felt like reaching out (in rare occasions) in the past for named services in DI, but meanwhile when running into this it rather makes me think twice.

  • @pavfrang
    @pavfrang Рік тому +1

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

  • @ryancookparagliding
    @ryancookparagliding Рік тому +2

    Not a fan of this feature - I don't like attributes and I reckon the enumerable route with a CanProcess() 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 🙂

  • @wojciechwilimowski985
    @wojciechwilimowski985 Рік тому +2

    Can you then inject Dictionary 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

  • @bladbimer
    @bladbimer Рік тому +1

    Good honnestly it was the major issue I faced with D.I and as mentionned below I was using factory methods or some functions to provide the good implementation based on key. I reallly hope this will be implemented everywhere (I mean for Worker services, API, Blazor etc.)

  • @StephenOwen
    @StephenOwen Рік тому

    Today we handle this by having an interface that all of our implementations use, one of which is a public enum value. Then our services that might need one or the other request an IEnumerable from Dependency Injection, and then in their constructor pick the one they need.
    It will be nice being able to do this natively!

  • @m_rray
    @m_rray Рік тому +1

    Thanks for the video! My only thing is that I wish you’d named the two keys and variables differently. I know it’s not the main focus on the video and it’s just an example in context, but ultimately people will, and do, use these videos as examples. Keying and registering services based on implementation is very leaky. Ultimately you have an interface and any concrete implementation could be used, the consuming code should not know about the implementation. I would generally prefer the use of an enum for example with the values of say “Primary”, “Fallback” etc, and then grab the primaryWeatherService and the fallbackWeatherService. They don’t care about implementation then.
    In this example, imagine you decided that the weatherApi was now your preferred api, you’d have two choices: one to swap the keys so that they no longer matches tbe implementation which is highly confusing, or go in and edit existing stable tested code to get it to use the weather one first and fallback to open weather - violating the open/closed principle.
    Using the generic naming all you’d need to do is, in the application root, switch the enum values around so Fallback is now Primary, and then that will be used as the primary, the assignment still makes sense, and we haven’t had to edit lots of code.

  • @jamesmussett
    @jamesmussett Рік тому +7

    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, Microsoft.Extensions.DependencyInjection.Abstractions).
    You can achieve pretty much the same thing, albeit not as elegantly, using the factory pattern.

  • @AhmedMohammed23
    @AhmedMohammed23 Рік тому +2

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

  • @snapching
    @snapching Рік тому

    I would add that this is a component or plug-in architecture. My preference is to have an attribute on the interface which provides the "key" differentiator. Concrete classes would then define the key rather than the class key provided during registration with the IOC container. Then do ienumerable of the interface in constructor, and in code select the preferred implementation based on business logic.

  • @Arcadenut1
    @Arcadenut1 Рік тому +2

    If you're looking for fail-over then I would expect the IEnumerable would have been a better solution. You try the first item and if it fails you move to the next. Doing the Keyed services you have to know ahead of time all the different services that were registered.

    • @andrewmcclement4464
      @andrewmcclement4464 Рік тому

      Perhaps this is better if you want a strict priority between the services, i.e. you care about the ordering.

  • @dimitrishadrin
    @dimitrishadrin Рік тому

    I needed this feature always.
    And now I use factories or implement my version of keyed wrapper.
    In new implementation I don't like attributes, in my opinion it should be configurable without attributes.
    And fully agree about decorators and convention.

  • @jwcodendaal
    @jwcodendaal Рік тому

    Hooray. Been using that for years in autofac. Used a factory recently to acheive that in one project. Nice to have it there now though

  • @Igahwanoiar
    @Igahwanoiar Рік тому +1

    IDK, I used to intuitively tend to solutions that had keyed registed services, but it never really felt right. The whole point of interfaces is that the dependency is on the service, and the implementation doesn't matter. But if the client needs to pick which registered service is used, doesn't it technically make it a different service?
    I started using this other methode. It's fair to call it boilerplating, but I wouldn't call it a workaround: just make an empty interface for every named service and have it extend the original interface. No magic strings, no assembly scanners and you get exactly the service that you need, using regular DI.

  • @TechAndMath
    @TechAndMath Рік тому +4

    In the context of my daily work, we solved this from a business perspective and never need two have multiple service suppliers for the same piece of data.
    1. Make sure that our service supplier is not just running the service on one server (can be done via SLA or contract discussion)
    2. Make sure that our service supplier has a SLA which guarantee a level of up time and performance time that is acceptable for us
    As our service suppliers are mainly fairly big boys in their field, we haven't really seen the need to plugin an alternative supplier up to now. Some times their system may go down, but that is within their SLA and our SLA, so it is something can be tolerated.
    Multiple suppliers always trigger data consistency and its own maintenance issues. So I feel that it is better to be handled by business perspective.

    • @PereViader
      @PereViader Рік тому

      I don't really think saying "we avoided the problem" is the same as coming up with a solution 😅

    • @TechAndMath
      @TechAndMath Рік тому +1

      Basically the business made the decision or solution and it worked better than a pure dev lead one in this case

  • @egida6486
    @egida6486 Рік тому +5

    @nickchapsas
    Don't you couple your method implementation, to a specific service implementation that way, which defeats the purpose of IoC?
    Keyed Service seems like a step backwards to me.
    For such usecases I'd prefer the "inject IWeatherServiceFactory" approach, and put the service selection logic into the factory, instead of relying on a DI-Container feature.

    • @diadetediotedio6918
      @diadetediotedio6918 Рік тому +1

      Nah, this is just untrue. See, the same key can be static and the concrete implementation be different, you can use it as a guide.

    • @egida6486
      @egida6486 Рік тому

      @@diadetediotedio6918 if the key is used (reused) it means it is a marker for an abstract concept, which would justify its own interface. if its used to pick out specific implementation from the container, its the service locator pattern and IoC is voilated. Keyed Services still seem like a step backwards.

    • @Nworthholf
      @Nworthholf Рік тому

      @@diadetediotedio6918 if anything, it makes you incapable to mock the selection logic anyway

  • @Fred-yq3fs
    @Fred-yq3fs Рік тому

    I used to like the autofac keyed service. But... resolving one implemention is part of the application logic, so it should not be part of the container.
    In that case, I prefer to use a factory so I'm explicit about resolving. Sometimes the factory is as simple as a key, so granted: factory might be overkill... but more often than not logic creeps in, and that's when factory is the right approach. You really don't want to let app logic creep in the DI setup, especially because it's harder to unit-test. Think refactor cost and risk: if not covered by a unit test you might not dare to refactor, so you're stuck in legacy code, just because you chose to use a fancy feature at some point.

  • @mistermeua
    @mistermeua Рік тому +1

    I can not find a good case where i could use this feature for any of my projects now or in the past.
    I feel itching that we are to spread constant strings around (looks "meh" as for me no matter how you wrap it...)
    As for me, I'd better inject some factory that relies on some non static (e.g config) props for resolving stuff I need. As other comments suggest.
    But maybe I just don't yet faced a case when I would need it...

  • @VeNoM0619
    @VeNoM0619 Рік тому +1

    The less 3rd party dependencies the better. Especially if its native C#, since importing 3rd party dlls etc. into cross platform apps can be difficult still (Unity3d).
    I feel like the native libraries are part of what makes languages take off. Python comes to mind, even though honestly I never fully touched it, but everyone says it can do anything. I also recall some unix libraries having that same adoption rate when they became a jack of all trades.

  • @lyrion0815
    @lyrion0815 Рік тому

    I would register the OpenWeatherMapService without a name, and the fallback one with "fallback". The OpenWeatherMapService would have to be registered last so its made the default by the IoC Container. Depending on the use case I would just resolve an IEnumerable and use the data of the first one that works.

  • @emerynoel567
    @emerynoel567 Рік тому +1

    This does not seem very useful to me, since the key is hardcoded into the injection site. What would be preferable is logic to determine. I have worked on a system with "tenants", where tenants may have their own services. (This was for an insurance company, where each 'tenant' had their own policy system, so we needed to inject services depending on who was calling.) We were able to achieve this with a non-default container, but I don't remember which one it was. EDIT: It was "autofac" I think?

  • @joeldickson5471
    @joeldickson5471 Рік тому

    You could do this before using a generic in the interface like an enum. I think it's more advantageous if you could use the key inside the ctor so you can use do something like use a database value for the key to dynamical change it.

  • @maximer.8190
    @maximer.8190 Рік тому +2

    What about creating two interfaces IWeatherApiService and IOpenWeatherMapService, both inheriting IWeatherService? If you have to specify the key where you require the service, it looks the same to me.

  • @Jadenity
    @Jadenity Рік тому +1

    What perfect timing for this. I have a situation that out came up that this fits perfectly. However, I'd like to know what the best way to do this in .NET 6 is (without using AutoFac).

    • @alexisfibonacci
      @alexisfibonacci Рік тому

      I make my services implement a key and then add them to the DI using add multiple. Then when needed GetServices() which returns an IEnumerable which I can then filter with LINQ and pick out the desired one. He went through that around 5:00

    • @Didymus888
      @Didymus888 Рік тому +3

      Just use a factory

  • @miguelduarte75
    @miguelduarte75 Рік тому +1

    Can't think of any use case for this feature but thanks for sharing

  • @sua01n
    @sua01n Рік тому

    Great improvement, now I can replace my own written workaround with that :-)

  • @trevorwilson7012
    @trevorwilson7012 Рік тому +3

    Personally, I liked the fact that they didn’t implement this feature. If you’re going to bind to something concrete you might as well just bind to the concrete type, rather than use a key to bind an interface that binds to a specific type. This just seems backwards to me. Thanks for sharing though.

  • @Kevmoens
    @Kevmoens Рік тому

    The one feature I didn’t like was if you need an inline resolution you have to pass the IKeyedServiceProvider through the constructor verse having a way to get a resolving class that is by the interface so you can see in constructor what your dependencies are

  • @SlackwareNVM
    @SlackwareNVM Рік тому

    "I wanna know your thoughts on the topic."
    Me: ... FINALLY!

  • @traviscoleman8810
    @traviscoleman8810 Рік тому +1

    This feels like a very complicated way to have interfaces, but work around them. Also, this looks like it would make for messy unit testing.

    • @kabal911
      @kabal911 Рік тому +1

      Don’t see how this in anyway affects unit testing, considering that wouldn’t even run though the DI container, and you are still mocking an interface

  • @logank.70
    @logank.70 Рік тому

    I was really hoping it was interceptors. It's really nice being able to add logging to things without having to pepper my code with log statements everywhere. That's the one feature I'm still waiting for.

    • @ldlework
      @ldlework Рік тому

      Use the decorator pattern (I don't mean "decorators")

    • @logank.70
      @logank.70 Рік тому

      @@ldlework That does definitely work. It's just nice to be able to define something like logging or performance metrics and be able to attach it to whatever you want without having a bunch of decorators for the different things you want to decorate. Although that could be a naive approach to decorators.

  • @sumitsinghdeo5466
    @sumitsinghdeo5466 Рік тому

    Its really nice one, I request you to have a session on IAsyncEnumerable implementation and usage.
    Thanks for sharing the knowledge with us.

  • @samysammour9983
    @samysammour9983 4 місяці тому

    Finally! It took a while though but hurray.

  • @lyrion0815
    @lyrion0815 Рік тому

    For me, this features is an absolute minimum requirement for a IoC container (along with a few other features the built-in IoC is still missing), I just added autofac to my projects as soon as I had the requirement for it.

    • @Nworthholf
      @Nworthholf Рік тому

      ..but this "feature" is a horrible antipattern that should have never made its way to msdi

  • @gamedev_expert
    @gamedev_expert Рік тому +6

    Hi! Don't understand the reason to bind two impl for one interface and then resolve by key. For me it's look like missing L from SOLID.

    • @diadetediotedio6918
      @diadetediotedio6918 Рік тому +1

      You don't need to force everyone to chase your rules when programming bro, just don't use it. But it is not incompatible with SOLID at all, this is good for the O, I and D of SOLID.

    • @qj0n
      @qj0n Рік тому +1

      Adapters to external systems don't always conform with SOLID, it was meant more for domain level IMO
      Better example would be to have multiple queue clients connected to different queues - since you use one library, they all have the same interface, but are configured in different way

    • @gamedev_expert
      @gamedev_expert Рік тому

      @@diadetediotedio6918 not forcing, but trying to learn something new

  • @notoriouslycuriouswombat
    @notoriouslycuriouswombat Рік тому

    so the default is always the last registered? kinda wish there was a way to register, and say "this is the default" that way the default is not position dependent

  • @detach8
    @detach8 11 місяців тому

    Isn’t the whole point of DI to remove dependency on specific implementations? By adding keys/removing interfaces we are making the code tightly bound.

  • @samuelschwager
    @samuelschwager Рік тому +1

    Pro: no interface inflation; Con: magic strings. I guess whether or not to use it depends on the use case.

    • @Nworthholf
      @Nworthholf Рік тому

      Its fairly simple - you should never use that thing.If you are going that way you can just as well instantiate everything in place without using DI container at all.

  • @haxi52
    @haxi52 Рік тому +1

    It really feels like the consumer knows too much about the dependency using this feature. As other have pointed out, there are better ways. I would like to see a real work production app that would use this. To me just have two interfaces that implement the 'base' interface and register those. Seems like a cleaner implementation.

  • @lucasterable
    @lucasterable Рік тому

    Complete my foot! Wow they still havent implemented:
    - decorator pattern
    - interceptors
    - named instances
    Amazing how behind .NET DI is in 2023

  • @Astral100
    @Astral100 Рік тому

    Wow, finally, I've been waiting for something like that for ages!
    It always bothered me that you can't use interfaces as they were originally designed to be used to in the Microsoft's dependency injection.

  • @matthias916
    @matthias916 11 місяців тому

    i always used factories for this, if im understanding this correctly this would be just like having a factory except you dont have to create the instances yourself?

  • @TonysRacing600
    @TonysRacing600 6 місяців тому

    I still don't understand the benefits of those Singleton Services over static variables you can reference.

  • @nikitaklimchuk5032
    @nikitaklimchuk5032 Рік тому

    I don't like service being dependent on DI container, especially these Attributes, it doesn't allow you to reuse a service using another implementation of interface. Also, it misses "rules" functionality comparing to DryIoc, for my use case - I want to reuse a service using specific key, and I want all nested services to be resolved using that key, doesn't look like it will be possible to do.

  • @garcipat
    @garcipat Рік тому

    This does not seem to do the same as injecting IEnumerable and filter since you cannot chose on runtime what implementation you want. I often have an Enum wnd different implementation depending on that. Means I need a Func to get the according service during runtime because it may comes our of the configuration or so. Its one Step late.

  • @florent9555
    @florent9555 11 місяців тому

    Hey, could you not solve your singleton problem with generic interfaces? For example IWeatherService and IWeatherService

  • @nertsch77
    @nertsch77 Рік тому +1

    Probably, the title is wrong. It should be called "DI-Container is now complete..." since Dependency Injection does not require a DI Container

    • @nickchapsas
      @nickchapsas  Рік тому

      Obviously but titles after a specific length are cropped and DI isn’t picked up by SEO so it had to be that

  • @tryagain6148
    @tryagain6148 11 місяців тому

    This feature also existed in Unity DI (still Microsoft), and it was way over abused. I prefer to avoid this kind of scenario by adding an identifier to the interface I'm registering that is implemented by the implementation class. In this way, I can do the factory filter in my code... without relying on the DI to do it for me.
    You know you're screwed when you notice that over 400 instances are name registered in just a simple application.😂

  • @everyonesview
    @everyonesview Рік тому

    I have been using Autofac for this until NET 8. Grand! One less external dependency.

  • @allinvanguard
    @allinvanguard Рік тому +9

    About time, this essentially means that other DI providers like Autofac are basically obsolete since they only provided keyed services and some very advanced and exotic use cases like interceptors. But right now, for 99% of my apps, I wouldn't really need these capabilities anymore. Happy to see this in MSDI finally.

    • @johan.mp4
      @johan.mp4 Рік тому

      Intereptor/decorator can be achieved in the standard container with just a few lines of code. You don't need a separate library for that.

    • @allinvanguard
      @allinvanguard Рік тому +1

      @@johan.mp4 Oh, my bad, I meant "Missing binding interceptors", not plain interceptors / decorators. That works pretty well already.

    • @catq_q6233
      @catq_q6233 Рік тому +4

      It's a step in the right direction, for sure, but for Autofac to really become obsolete MSDI still needs to add a lot imo, starting with property injection...

    • @Rein______
      @Rein______ Рік тому

      ​@@catq_q6233please no property injection 😢. Does not work right with nullable

    • @celluj34
      @celluj34 Рік тому

      @@catq_q6233 You cannot convince me that property injection is not a code smell.

  • @robslaney3729
    @robslaney3729 Рік тому

    IMHO the "key" dependency is the wrong way around. It should be a registration concern, not the resolved. Same issue as base classes having to know about their derived.
    Worked successful with Unity DI container for years, the resolved instance never had to worry about which implementation it got. Think about conditionally adding a cache wrapper. By using an attribute I have effectively decided at compile time which one I get.. or I change the registration to match what is being requested.. which basically defeats the purpose.
    Can I use the keyname when I resolve, like with ActivatorUtilities, or registration factory lambda?
    Will resolving IEnumerable retrieve all the instances, both non-keyed and keyed.
    If I resolve without the key attribute, will the existing behavior apply that includes key-ed registrations in the last-in best-dressed eval?
    The biggest issue I have with MS's ServiceCollection is the lack of ability to use a factory with an open generic. You can't get the type(s) being used to resolve. Not common, but very handy when the need arises.

  • @ldlework
    @ldlework Рік тому +2

    Doesn't this violate the open-close principle? The dependent class now needs to statically refer to its dependency (albeit through the key alias). If you wanted to change the behavior of the dependent, you'd have to come in here and modify it. I think in a real scenario, you would not be using the key to identify specific concrete implementations, but rather "flavors" of implementations, like "cached" or "uncached" as you mentioned.

    • @Nworthholf
      @Nworthholf Рік тому

      It absolutely does, and if you are using that you might just as well do back to instantiating everything in place

  • @ukapas
    @ukapas Рік тому

    Is there any compile-time protection from mistyping the key?

  • @_iPilot
    @_iPilot Рік тому

    Does it support injecting multiple implementations as IEnumerable of the same key if several is registered with that key? Or keys must be unique?

  • @alexvanheerden5702
    @alexvanheerden5702 6 місяців тому

    Strings passed to Attributes must be constants, so how is this different from just injecting the concrete type?

  • @kenjohnsiosan9707
    @kenjohnsiosan9707 Рік тому

    thanks and, you didn't show how it would automatically resolve services at runtime when say for example the first service fails.

  • @daddy2claire
    @daddy2claire Рік тому

    Resolving a service by name.... isn't that Service Locator - usually considered an anti-pattern?

  • @qj0n
    @qj0n Рік тому +1

    MSDI was always meant to be a simple DI interface, which you can easily override with your own implementation, so you can easily use any container you want. I'm worried that if they add more and more features to it, it will be harder to replace it or some features will be missing with some implementations. It would defy the whole reason to create MSDI

  • @alextelegin6467
    @alextelegin6467 Рік тому

    Well, 10 years later we might get brand new features like dependency validation and captive dependency check)

  • @temp50
    @temp50 Рік тому

    I'm not sure if I get it. If we are acquiring the given service by its actual type then why are we trying to register them by the interfaces they implements.

  • @parlor3115
    @parlor3115 Рік тому +6

    I prefer using generics in this case. Make the interface generic in T and then create any number of empty classes and inject your service using those. The advantage I guess is that you don't have to wait for this feature and don't have to create a static class to contain all your keys.

    • @diadetediotedio6918
      @diadetediotedio6918 Рік тому +1

      You still have to create any number of empty classes and inject your service using those.

    • @parlor3115
      @parlor3115 Рік тому

      @@diadetediotedio6918 You have to create any number of keys using the new approach too which means you'll have to create a static class with const / static fields to use as keys. The generic approach is cleaner IMO as you don't have to clutter your methods with the "From" attribute.

    • @DotNetCookbook
      @DotNetCookbook Рік тому +2

      Wow that's ugly

  • @Crozz22
    @Crozz22 Рік тому +1

    async is a pretty big thing missing

  • @Nezbo
    @Nezbo 10 місяців тому

    Is/will there be a way to get TService[] allOtherImplementations without having to know the keys of other services registered under the same interface? Or Dictionary with the keys.

  • @KJ1987pl
    @KJ1987pl Рік тому +1

    Yeah, for me is great news, generally, I'm always against adding big libraries to projects, especially when they have native support, less dependence is better for all of us (I'm talking about programming only 😅)

    • @RamonDeKlein
      @RamonDeKlein Рік тому

      The question is whether you should use this feature like this. Creating an intermediate provider interface would make much more sense and also makes testing easier.

  • @eclecticx
    @eclecticx 4 місяці тому

    You have good and informative videos, but you should seriously consider a slower cadence. As for this example in particular, it seems to break SoC (antipattern), has some naming issues (implementation rather than purpose), and depends on hard-coding.

  • @bradenb
    @bradenb Рік тому

    I don't get the usefulness. I want my interfaces to hide my implementation, so why add an attribute (also yuck) to then request a specific implementation? I'd much rather just inject the concrete type, or even better, just have a more specific interface. Another commenter mentioned that this could be for purpose rather than implementation; fine, but is this really the way to solve that problem? You could have a service dedicated to providing instances dynamically based on purpose rather than tie purpose to a compile-time decision. What's the use case for this?

  • @leonardomoreno23
    @leonardomoreno23 Рік тому

    Goodbye Scrutor. It was a pleasure ❤

  • @ahmedbenissa5982
    @ahmedbenissa5982 5 місяців тому

    This was still achievable before .net 8 using factory lambda expression

  • @yurymatsiulka5786
    @yurymatsiulka5786 Рік тому

    I prefer to work with keyed services when an entity or contract has a discriminator

  • @colindawson4818
    @colindawson4818 Рік тому +8

    I'm going to stick with AutoFac. I've used it for many years, without issues, and don't see any compelling reason to change. The ultimate king for me is performance, and not just at a theoretical level. I'd need to see a practical implementation where it actually makes a big difference in a real world scenario.

    • @user-dq7vo6dy7g
      @user-dq7vo6dy7g Рік тому +1

      stronginject might be a good thing for performance. I understand he/they do the injection at compile time.
      If you take a look let me know, I have this in my bookmarks for months 😄

    • @user-zk5ym9ut1j
      @user-zk5ym9ut1j Рік тому +1

      I've seen benchmarks thay show that built-in DI is faster than Autofac

    • @ricmrodrigues
      @ricmrodrigues Рік тому +1

      Main problem with that, which I want to as well, is they've made the built-in in such a way you can't take it out. A bit of a conforming container and a leaky abstraction.

  • @paulalves966
    @paulalves966 Рік тому

    Castle Windsor DI have that for at least a decade! 😌

    • @paulalves966
      @paulalves966 Рік тому

      More 10 years and we may get mixins, property injection, conventional registrations, and MEF 😂🎉

  • @niranjanjyothi2416
    @niranjanjyothi2416 Рік тому

    How about the existing NamedSingelton?

  • @Gallardo994
    @Gallardo994 Рік тому +2

    To be fair, I dislike built-in container simply because Ninject feels nicer to use in any dotnet application, even console one. It is a one-liner for the initialization part, and it is easy to split code with modules

  • @pranavraj4655
    @pranavraj4655 Рік тому

    I love this change. 😊

  • @Bliss467
    @Bliss467 Рік тому +1

    You started by saying .NET added a feature for DI which is one of three you feel it’s missing. Wouldn’t that make DI in .NET incomplete, to the contrary of the video title?

    • @nickchapsas
      @nickchapsas  Рік тому +3

      Just because I wish I had them doesn’t mean they are missing, while this one was widely requested for many years

  • @Crossbow123
    @Crossbow123 9 місяців тому

    Its still missing property injection. Now that we have required properties, this feature is begging to be here.

  • @AndersReinhardtHansen
    @AndersReinhardtHansen Рік тому

    Could this be used in conjunction with featureflagging, where you would use versioning or some other functionality to choose which service to use?

  • @martinwidmer5961
    @martinwidmer5961 4 місяці тому

    Keyed services is nice but where are DependencyOverrides ???

  • @istovall2624
    @istovall2624 Рік тому

    am i not understanding correctly?
    i use the service locator pattern that way the specific logic to determine which one is returned can be user/client/customer/use-case/business specific.
    is the key, different/better? i think it's worse.

  • @joephillips6634
    @joephillips6634 Рік тому

    it's about time

  • @Ehomer0815
    @Ehomer0815 Рік тому

    Just bought your new course but it doesnt work :(
    Only white screen after starting it

  • @TheOneAnOnlyGuy
    @TheOneAnOnlyGuy Рік тому

    Complete? Did they add decorator support as well?

  • @riccarrasquilla379
    @riccarrasquilla379 6 місяців тому

    thanks for the video

  • @ivandrofly
    @ivandrofly Рік тому

    Good one, thanks :)