The .NET dependency injection methods you are not using
ฝัง
- เผยแพร่เมื่อ 22 ส.ค. 2024
- Use code DEPS for 20% off (first 50 people): dometrain.com/...
Become a Patreon and get Discord & source code access: / nickchapsas
Check out my courses: dometrain.com
Hello everybody I'm Nick and in this video I will introduce you to, hopefully, a few dependency injection methods for the built in IoC container that you might not know about. Some of the names can be deceiving so once you finish watching this video you should know everything you need to write some awesome IoC code.
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 #dependencyinjection
Thank you for this, I finally understand why my multitenancy configuration wasn't working as expected 😂
Don't worry I learned it the hard way too 😂
Could you please recommend any materials regarding multi-tenant systems? I've worked with one too little to really understand how to deal with stuff in this case.
This came at a perfect time for me! I've been working on a shared library that uses DI, which has dependencies on other objects being registered, so we want to register defaults but have them be overrideable. Thanks for the great explanation!
Singleton/transient/scoped all refer to the lifetime of the implementation, not to the amount of implementations for a abstraction! Consider the fact that you can register both a singleton and transient for the same abstraction and it still works as expected: the singleton is shared and the transient is unique for each branch of the object graph. Even if they are delivered behind a IEnumerable. This technique Nick is showing is incredibly powerful coupled with the strategy pattern. Keep up the good work Nick, love your videos! Great length and very focused.
I am using .NET DI for years and this video still has a hell of things I did not even know to exist. Thanks
Had this requirement years ago and what I did is a trhough a factory and some custom hack way of doing this, I wish I had seen this before, this really is great find! Thanks Nick!
My favorite part of this is how concise and straightforward this implementation of DI is, and how you can tell exactly what your code is doing just by looking at it.
straight to the point, no bs, fantastic!
A bit fast explained for the level I'm at.
BUT... I really like the way you explain it. So, I subscribed to the channel.
Just wish I can like each of your videos 1billion times.
I have learnt a lot from you.
Thanks.
I discovered those overloads while building reusable libraries. Very helpful because you don't want the end user of the library to accidentally register the same service multiple times.
Dependency injection is awesome! 👍 The company I work for, use it very well.
FOLKS, you should learn it!
This mess is why I prefer Simple Injector. Simple Injector fails early if you try to add multiple implementations for the same service. Unless that's specifically what you want to do of course, in which case there's a separate method for that. This way there won't be any nasty surprises down the road.
Also the fact that Simple Injector can (and by default will) Verify its container registrations is 👌💯
Thanks for this video. I like it because I use the built in dependcency injection in a lot of projects.
I'm still not sure what's the better way to wire things together - the explicit model (as seen in the video) or the annotation- and classpath-scanning driven approach used by Spring Boot. I've used both extensively, and saw success (and failure) with both.
Yep that's not obvious behaviour ! Thanks Nick keep up the good work
Is there a way to override the instance used in the DI container?
In Unity I believe that there is something called DependencyOverride, where you can supply the instance yourself effectively overriding what's already registered with the container?
That would be useful.
As nick mentioned, all the `services.AddScoped();` things are just extension methods to add things to a service collection, which is essentially a dictionary.
You can simply use it as a dictionary and use
var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(IService));
services.Remove(serviceDescriptor);
Thanks for this thorough explanation.
Very intresting video, what's the annotation tool you use btw?
Also sounds a lot as a "works by an accident" type of scenario :D
Great analysis. Thank you Nick.
And the main question is how to set up DI to inject a specific implementation of one interface from several available implementations. It's rather strange that this famous library can't do such a simple thing :) Without this feature, it is very difficult to use this library.
Yeah, I agree. Most DI containers allow you to have named registrations. It's too bad MS do not support those, especially as Microsoft.Extensions.DependencyInjection.Abstractions is now the main abstraction over DI containers...
Could you clarify what you mean by this, and in what use case it would be helpful? It sounds like you want to do addX and addX, but then have some means of specifying at GetService time which of S1 or S2 you want it to return. Is that correct?
If so, I believe that can easily be worked around. For example, to ensure that a given interface returns S1, you can just 1) define a tag interface ITag, 2) register the desired service as itself (e.g. addX), and then 3) register the tag service with a factory lambda that fetches the S1 service (addX services.GetService()). That way, S1 and S2 are still bound to the original IService, but you now also can request S1 via ITag AND keep consuming classes from having any direct dependency on the concrete S1 implementation of IService.
@@MrEnvisioner There's no way to do it the easy way without any extra effort or hacks. But quite often one interface has many implementations.
I believe the problem here is not with the features of the library, but with desire to misuse it. Look at this from this perspective: If a class depends on a specific implementation (e.g on a ColourPrinter), then it's a mistake to have it accept a generalized abstraction (IPrinter). If it truly depended on abstraction (IPrinter), it shouldn't care which implementation it got (MonochromePrinter, NullPrinter, PdfPrinter). If it cares (it must be a ColourPrinter), then perhaps you could improve your interfaces to reflect that (IColorPrinter inheriting from IPrinter and make class depend on that instead of IPrinter).
Like Robert above me said, this has bad design smell all over it for me. If you want a specific implementation, than register it as a different interface and resolve for that, or resolve for the implementation directly. If you are trying to resolve for a service and you care about the implementation that you get, than you should be changing what you’re resolving for.
There’s a reason why the Options extensions support named options/config, but the DependencyInjection extensions don’t support named services. It’s obviously doable. But the former makes sense, the latter doesn’t.
How properly resolve the different implementations of the common type?
I was hoping for some cool way to distinguish which `IService` is injected which would be far more interesting IMHO since you can have few `IService` implementations for lets say authentication, authorization, translation etc. . I'm currently in that situation where I have to register multiple `IService` with different implementations and then use factories to determine which ones to load and when and ... I'm not gonna lie, it's a fking pain in the ass to create all of those factories, checks, guards etc.
If you know any way to improve this kind of chaos I would be greatly interested in seeing it.
Thanks for making these videos and keeping it informative
You can make interfaces that inherit IService and use that instead of your factory, if you really need to have them share a same interface.
@@sergiofonseca2285 That's what I basically do right now. I have a factory that gets `IService` enumerable from MSDI and then, based on the value in the header I return specific instance which is really annoying when you have more cases of that kind. I ended up making up a generic factory because the real difference there to return a proper `IService` is determined by the header value
What is the highlighter you use to draw rectangle and points on the screen
add ServiceOne twice , maybe we see two ServiceOne in container ,so it looks not singleton, but singleton is not be definition by what we see, i think it still work in container, because container will always resolve second ServiceOne ,and it's singleton
and i know it's not good behavior to write, but it also wrong be say not singleton.
Nice and useful video, thanks!
I think you could have demonstrated this easier with a simple console application instead of an Web api and Postman. 😉
Thanks for the content. I learned some useful things from this. 🙂
It would be easier indeed but my reasoning was that more people have seen DI in the context of an API than a Console app
Nick I've enjoyed the videos you've been making very much. Could you please alter your homepage so that the bundle includes all three of the packages you're currently offering, rather than just two?
Bundles are logical groupings. There is no bundle that makes sense to have all the courses. When my Integration Testing course is out, the Essentials Bundle will go away and the two testing courses will become a new bundle
@@nickchapsas I see what you're saying. Maybe later when you have a dozen or so series, a kind of All Pass will make sense. Anyway, considering all the great free content you've made, I won't feel bad about buying any or all of the series you've made so far at their current price point.
What's the logic behind the TryAddEnumerable naming?
Couldn't find any no matter how hard I looked
Very insightful video
Will it be better to use Factory patter instead of registering two different services?
Hi Nick, great video, thanks for this.
I have one question, I am using a class library where I need to inject multiple services of the same type. Just wanted to know how to write a nunit using moq framework for IEnumerable
From what I’ve seen with Nick is he uses nsubstitute and tries to avoid using registration in the test project. I’ve tried where I create a bootstrapper in a test project but I think we have created objects with too many dependencies. We are working on making smaller classes to try to avoid this headache
Hi Nick, great video as always, so thank you for that!
I'm developing a WPF application with Microsoft DI and have a hard problem to solve:
I registered all my services for the application itself and get my MainWindow and it's ViewModel via DI. Nice.
The application can open project files. So my IProject is registered as a scoped service such that all dialogs that possibly need it get the same instance.
Now the project itself has its own kind of scope, a sub scope within the lifetime of one IProject because you can play and reset the project and on reset i need new instances of my services.
How would you do that? Currently I am running the service locator pattern: i have registered my ISubscopeServiceProvider as a scoped one and obtain a new one with every SubScope witch in turn is able to create my my new scoped services....
Maybe you create a video of Ms DI for non-trivial use cases?
Keep up the good work!
One thing I still haven't found an elegant solution for is selecting an implementation at runtime. Think a strategy-pattern for example.
For example you have two implementations of ICountryRules .. Would the best option be to create a factory where you inject IEnumerable and then have a dictionary of the implementations?
I have implemented my own named instances for my stuff. Maybe that's a good idea for a video actually
@@nickchapsas Yes would really appreciate it.
I was about ask this in the comment
will that behavior of tryaddsingleton will not change on the future? because all I know is MS has unsettling minds and changes anything that has some breaking changes.
Currently Im working on a College project that uses c# and i'm trying to implement a pluggin architecture that will load assemblies found in a especific folder at runtime. They all implement the same interface, so has the DI any method to tell it to load only the IServicesX implementations from those assemblies ?
As far as I know the default container doesn't have that capability. The keyword you're looking for is Assembly scanning.
There's this other video by Nick explaining it: th-cam.com/video/_YkvFQ1-Lt0/w-d-xo.html
@simple gameplay - as @Foam says, the built in container does not implement assembly scanning, and for very good reasons: MS wanted to keep it lightweight and fast and they've succeeded by and large. However there are libraries that will do what you want. Certainly AutoFac does (and I'm sure other DI containers), but that means you have to use AutoFac (or other) instead of the built in container, which may be fine for you, but personally I use Scrutor on top of the built in container which offers some very powerful options for assembly scanning that you can use to treat different parts of your project in different ways. You might want all interfaces in a specific namespace to be singletons, for example, or to have different duplicate behaviour. Scrutor allows you to use selectors to do this sort of thing.
Some caveats: It's very powerful and can make your DI registrations very clean, but it can be very easy to over-complicate things with selectors and whatnot and to someone coming in cold, it can look like a lot of magic so be aware that you might want to add comments, but if you have too many then you're probably doing it wrong. Also, with manual registration you can often catch missing registrations at startup when running in your development environment (try commenting out a registration and you will see that when you try and run your project you will get an exception telling you a service can't be resolved), but with a library like Scrutor, although you can often add services and they just get registered automagically using whatever default behaviour you have defined, it is possible for services to get missed if you have complicated rules set up, and this will not manifest until runtime when something tries to use that service and you may have already deployed to production(or at least your QA/automated testing might fail and then you have to debug and find out why which costs time). Note that in both cases it is possible to compile and deploy your application with missing registrations and in both cases it will fail at runtime when you first try and use your app, but in general you do have a better chance of catching these problems with manual registration and running your app in debug to check things are working before committing code.
Like all libraries designed to take away some drudge work, there are positives and negatives and it's up to you to understand those and weigh them up against your use case before using any library (just look at the love/hate relationship the community has with AutoMapper, for instance). One use case really springs to mind and that's serverless applications: Be very mindful of implications on cold start times with any library that does things automagically. But use it wisely and Scrutor can be a very powerful tool that means you don't have to have reams and reams of DI registration code in your startup. It's a few years old now, but Andrew Lock has a great primer blog post on using Scrutor.
Happy coding!
Hi, what tool do you use to draw on the screen?
How did you get this idea.. ? 😮
Nice video thank !
What keyboard are you using ? I have changed for a Logitech MX Key mini but I have lost so much dexterity ...
I'm using a Logitech MX Keys
@@nickchapsas I am impressed of your typing speed !
Hi Nick, thanks for the great tuts. I've got one question related to the courses you're selling. Do they get updated to reflect new versions of .Net once they are purchased?
They do only if there is something actually do update them on. For example my Minimal API course will have an update in .NET 7 because of the new features. There is nothing in Unit testing or dependency injection that is changing in .net 7 so no planned changes there
I can't figure out when you would want to register more than one implementation of a given interface. If anyone knows, I'm curious about that.
Multiple reasons. Decoration, conditional resolution, pipelines just to name a few
Nice video. I have experienced this myself. I have a question though, if you have multiple service implementations registered in DI, at the time of injection what method do you use to select the correct implementation for that service. We have four IUtility implementations and it was, as you pointed out only selecting the last one. So I wrote a resolver and injected that, then selected the correct implementation at construction.
A ServiceDescriptor also takes a factory method, you'd have to pass TService and the factory method. This also works for Add and TryAdd. This way, you'd still simply inject TService and your factory method will be called to decide what implementation to inject. You can also just make a factory service , inject that and call something like .Create on it
@@Vinoyl Yes, this. Or add a tag interface to the desired implementation and register is alone using the tag interface. Or, if you can't modify the definition of the class, add a tag interface and bind to it a factory lambda that returns the desired instance.
@@Vinoyl Can you please elaborate more?
What are some good examples on why you would register multiple implementations of the same interface? The only example I could think of is if you wanted to save to multiple databases, and I don't like this example. Your libraries and other projects would need to support injecting IEnumerable of the services as well(you could argue that should be the standard then for all services)
It's very useful for plugin interfaces. At the moment I've got some code that needs to pass data on to similar APIs hosted at different third parties - I need to choose the API at runtime based on the content of my data. My library project for interacting with those contains
* ISpecificExternalApi that exposes the relevant function on the API at an abstract level, and a name field
* An ISpecificExternalApi implementation for each API type
* IExternalApi interface that has the same function, but with an extra parameter for the "key" that decides which API name to use
* An implementation of IExternalApi that has IEnumerable injected, logic for selecting an API from that list and proxies the calls
* An extension method .AddExternalApis(this ServiceCollection) that adds all the ISpecificExternalApi implementations and the IExternalApi
This way my library is easy to use - just call services.AddExternalApis() in the service startup, and add IExternalApi as a dependency in my consuming code. If I need to add more implementations of the API, I only need to update the library creating the implementation of ISpecificExternalApi and the key used in the data. No need to update every place that calls it.
@@HaralHeisto interesting. I haven't delt with calling multiple APIs passing the same data. I guess you could use it as a messaging center as well. If you want a notification in Microsoft Teams, Slack, Email, Text Message, etc, you could do it that way as well.
@@DerekWelton You could, but be aware that opens you up to complications around partial success. I'm using that library in a messagebus based application - if an API call fails the transaction is failed and retries later.
If you're doing a broadcast to multiple systems, you'd need to add another layer of message queue behind your routing class. Given that, it's probably simpler to just have multiple subscribers on your SendNotification message and have them decide if they need to action the message or not.
I dont get why you would want to register two services from the same interface. What are the usual usecase of this?
Like Haral answered to Derek Welton who asked the same thing earlier; he is giving a plugin example.
I am using it in a system where my users are able to ship packages. Now we can ship with three, not to be named providers. At runtime the user gets to select which one to use. The solution is to have multiple implementations (concrete types) that all implement IShippingProvider. I will get a list of all of the registered types that implement IShippingProvider. Now I offer the user to select one. Then the code will just assemble the shipment data, with types that are also 'mine'. Then it calls a method that all of these concrete types implement CreateShipment(IShipmentData data);
If I need to support a fourth one... I will just start a new library, add a new class ShippingProviderFour : IShippingProvider { ... }. Obviously test it.. but can then just drop that dll there. Restart and the thing gets loaded and registered automatically and the fourth options shows up in the dropdown.
@@paulkoopmans4620 That makes a lot of sense, thank you!
This is the cli dependency injection command you ain't using but should be: "dotnet add package Autofac"
How about a video on DI with an AWS Lambda function. is it possible?
It is yes. My latest aws lambda video touches on that
@@nickchapsas Thanks, I will check out more of the videos. :-)
@@nickchapsas Which video? The rest api? is it available with the "function" type of lambda?
What's your IDE you're using?
JetBrains Rider
how... useful...
and now tell me how to implement dependency injection without the frustrating need to specify the type i want to register 3 times!
1st as a private readonly, 2nd as constructor parameter 3rd as an assignemnt in the constructor body
thank you...
i hoped c#11 would have a revelation for that...
this is so annoying, you know what i'm doing now? i'm actually converting all my classes to records, just to get rid of the constructor which is basically abusing the whole thing XD
but it works and there is no performance or memory downside so ... who cares. abusing rules
It was great geek 🤓
Great content, i went to you page, may i suggest course? api with queue(rabbitmq or ibm one), with polly and what is good practice since dont know firts or second good practice then carry on to my needs. sugested nugget packages:MassTransit,refit; another course wouldbe good hangfire or equivalent :)
Is all this in your DI course?
This part is mentioned in the course yeah
@@nickchapsas amazing, coz I have the course but i did not start it yet
9:12
Dude why are you not on Udemy?
Because it would be very bad for me if I wanted to actually make a profit
@@nickchapsas With the quality of your content you will easily reach hundreds of thousands student in Udemy for sure and will be much for affordable and accessible for everyone when they have sales 😀 You are the only DotNet professional I've seen giving advance turorial.
@@devian3634 Udemy doesn't work for people that already have na audience of their own. Tim Corey has tried it and he said he lost 30k on publishing a course there. The main problem is regional pricing.
@@nickchapsas Sad to hear that, well anyway, I will just purchase your course in your website instead if that's the case
Huh, the .Net DI container is really odd in it's opinions. I'd consider nearly everything in this video bad practice, but the thing I use all the time (Decoration) isn't supported by design. I guess I'll just keep shimming in SimpleInjector until the .NET DI container actually supports decorators.
Does the TryAdd… variant have a return value so you can see what happened?
Ha! I forgot to mention that but all of them break the usualy "Try-out-return bool" patteron by just returning void and nothing else. You can't know if it was added or not
Horrible. When I moved from .net to Java Spring, all these nightmares disappeared. Explicit stupid constructors? Lombok. Component registration? Single attribute. (annotation). All the mentioned things cleverly handled for you.
Lombok. The library that exists because Java is terrible. Spring boot. The framework hat exists because Java EE was terrible. Spring boot's DI. Because your classes should know how they are supposed to be registered. Nah, thanks I'll never go back to that.
I am sorry, but Microsoft's DI container forces you to use bad habits. Does not follow the fail-fast principle.
IMO SimpleInjector is a much better DI container up there. It has an opinionated way of registering objects in a non-conforming way.
A single object is a single object. Enumerable is one or more registrations and can not be zero and need to be registered especially like so.
"It is the best dependency injection content you're going to find anywhere"
Is it better than "Dependency Injection Principles, Practices, and Patterns" book by Mark Seeman?
If you're looking for something you will be able to use in the real .NET world right away, then yeah
Can anybody tell me how this could ever be used?
can you be more specific? :) You mean to ask why one would register multiple concrete types implementing the same interface (service)?
search for/look at answers to Clemens's and Derek Welton's question
Are a good guitar player?😁
I sure am 😀
Nice explanation, I've already had problems injecting same type twice! Thanks!