Dependency Injection in C#

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
today's episode was brought you by tonic tonic is the digital agency that's good for business go ahead and get a hold of them today on their website at hello tonic com what do video game consoles in dependency injection have in common let's find out next in the beginning there were handheld games and they were hard-coded then came the video game console which allowed us to switch the games this was an example of a real-world dependency injection did you notice here we can see the dependencies your class represented by the console and of course the DI framework so if we take that real-world example and apply it to our code here I have a class called handheld single game console and it implements the AI game console right now kind of never mind about the AI game console what you should be focused on is we have one method it's called low game and inside that method we have a hard-coded dependency and that game is Caterpillar and what I mean by hard-coded dependency notice that we're using the keyword inside low game and for me I call that trapping a dependency and what does that mean even further well that means there's no way for me to swap out caterpillar with a different game using dependency injection requires a certain mentality or mindset and we have to conform to a few rules in order for this to make sense the very first rule is only accept your dependencies through the constructor we can also use property injection but in general we're going to use just constructor injection secondly we're gonna avoid using the new keyword the new keyword will trap our dependency inside of our methods or classes and we're going to avoid using the new keyword when at all possible a few exceptions to that rule is when I knew up a poco model or if I'm creating a factory method or class so stop right there that's the part where my friend Pete Duncanson would say is that really necessary do you really need to swap things out and - Pete I say to you yes it's absolutely worth swapping out things because when it comes to unit testing you'll almost swap everything and the way we're gonna handle dependency injection is we're simply just gonna ask for what we want and it'll be magically filled so today we'll be covering two ways to get your dependencies injected through the constructor into your class the first one is called poor-man's dependency injection the second one will be using an ioc container poor-man's dependency injection is essentially a manual way of doing your dependency filling for your constructors whereas the ioc container is going to be an automatic hey i need this can you just give it to me so back into the code we go so as we see here this handheld single game console is what we're using and it's going to call low game so if we look at the single and held a game here it's game is trapped by this new keyword being used in low game so what we're gonna do is we're going to have a new console we're going to call it a Super NES it uses the I game console and it has the low game so rather than new up a game down here what we're gonna do is we're gonna create a constructor up here and we're going to say I want an eye game and we'll call it my game we'll use some resharper goodness here to do a pattern that you should be very familiar with in dependency injection essentially what you'll do is you'll have a private read only of your dependency and you'll initialize that down here so resharper as you saw lets me do it automatically or you can manually type that I would prefer to have something you automatic like that now when I mean in my low gain down here I just do game dots play game and this class here the Super NES doesn't know what game doesn't ship from from Nintendo so to speak knowing what game it will play just knows it will be an eye game now if we pop back out here to our class here it's gonna look very similar we're gonna do new Super NES and now it's saying hey I need an eye game it's at that point or at this point I can say well let's do Super Mario World or Zelda so if we do caterpillar this should all just build and whatnot so this is a very simple illustration of the poor man's dependency injection now you may be asking yourself well wait a second you're trapping these new keywords here inside your main method here in this particular program here and to that I would say at some point you have to actually commit to using your new keywords somewhere the thing with dependency injection is you do that in one spot it's basically where you register everything and there's a term for that as well it's called your composition route so in your composition route you're allowed to use your new keyword here so in our particular case here with a console app we have the composition route here if you're running a MVC site or web API there or there are places that are considered the composition route where you're allowed to use the new keyword now this will get very tedious here if we were to have say a more complicated game so caterpillar is a pretty easy game right so if we were to have a game like Zelda here which is one of my favorites let's say that this one here also needs some things it needs some characters maybe and need some weapons so we could do it one way which would be I would say the wrong way which would be new weapon equals let's see if we new up a weapon here maybe a sword yeah there we go whoops let's try that again all right so we have a sword here and now we can use the sword somewhere within this method or let's say we're in the play game method what not the the trouble here is again we're trapping that new keyword so what we would really want to do is through the constructor we would say hey I need to use a sword and it's an AI weapon and we're gonna use resharper again like spell right to auto initialize again that's the pattern you should be using there now we can use this weapon here and we can say attack and usually probably won't do that and the start method here so let's go ahead and put it in play game method here and go now link or we don't have link yet but we the game right now it doesn't know which sword you're gonna use so if we now pop back out here to our super ness and say let's do a new Zelda game it's gonna now require a new weapon so we're gonna do new sword as you can start to see here this is going to get out of control super darn fast so there is a different way to approach this and this is using default constructors so of course everyone has their own opinions of dependency injection what's good what's bad what's indifferent but one thing that gets tedious real quick about the poor man's method there as you can see you have to nest all these new this new this new this and it can get really out of control really fast so the thing that I would posit to you is that something called default constructors with poor man's dependency injection and what it is is it's like the Super Nintendo actually shipped with the game already in it but it still remains swappable okay so the goal here is to pretty much drop all these nested news into maybe something simpler that something simpler means let's go to the sword class here and actually we're gonna go to the Zelda class here I'll take that back and so when we depend on an AI weapon what we can do is add a second constructor and it's gonna be a default constructor notice I'm not gonna take any parameters here however the first thing I want to do here is do this and I'm going to call my other constructor here but I'm gonna provide it with a default item and yes this is where the controversy will be because we're actually using the new keyword here inside of a class however I would say that this is a pretty good trade-off between making it super abstract that somebody may not know what's going on and kind of you know indicating what you're actually using here now as far as coupling goes yes we've now coupled ourselves to a new sword but we can also reason that a sword is so intrinsic to the system that you would never not want a sword and it's native to the system so if we again pop out here to the Super Nintendo we need to do the same thing here we need to say you know what the Super Nintendo now ships with a default game so in the real world this would be you know and you get the Super Nintendo but it came with a game alright it came with Zelda we're all happy and again we're making that same trade-off here because now we're saying the Super Nintendo is somehow linked no pun intended to the Zelda game here and then if we come out here this still applies so this will actually be very helpful this syntax when we wanted a unit testing but now we can also do things like this we can say you know what I don't have to give you a sword so just use the default one and now I can also say you know I don't have to give you a game just use the Super Nintendo so now when we run this and at runtime we say game console download and let's go ahead and try that let's go ahead and hit run hopefully we'll just be playing Zelda just out of the box here oops and it flashed there on the screen briefly it's let's let's add one thing here to pause our app read key and try again bingo swing this giant sword killing Ganon if we want to look deeper in to make sure that we are using those default dependencies we can look at the sword swinging this giant sword and we can look into the Zelda game here where it says we're loading Zelda and we're killing Ganon alright so in general we lightly touched on what poor man's dependency injection and there's nothing magical whatsoever about poor man's it's essentially writing your classes in a solid way don't trap your new keyword because that's creating a dependency where you can't swap it out and then we realize when we acknowledge that poor man's can get real tedious if you have to do the new nested new dependencies all the way down there's just it's just something you can't manage so one off ramp that you can do there between using the next topic which will be the using the ioc container and poor-man's is to use those default constructors now keep in mind a lot of people will say if they're purists with a dependency injection they'll say ah but you're creating a coupling but to me on very simple projects or even medium projects it's a completely valid trade-off to say look let's make this very solid we'll be able to inject anything we need in here for a dependency injection or to alter our behavior slightly however let's just have defaults for a lot of these things because at runtime unless we're testing we're not actually going to switch out anything and now we get to the part that usually scares everybody when it comes to dependency injection mainly because they don't know on how it works and what I'm talking about is the ioc container so we've been learning about dependency injection so far we've been doing at the poor man's way or kind of manually filling the dependencies through the constructor so now what we're going to do now is look at a way that's going to automate the process through the use of a c-sharp object which holds on to your dependencies for you and that way when it's time to create anything that the container knows about it'll know how to get those dependencies okay let's illustrate some of the ideas we have our dependencies I game I weapon I game console we need to put those into a container and our framework will provide that to us our di framework then decides okay if we need to build something let's say an AI game console let's grab that let's look see what it depends on in this case I game and then that depends on an AI weapon and what it does is it goes the container and gets an instance - each one of these and there we go alright so to recap what we're gonna do is we're gonna download what we call it an ioc container and there's a bunch of them out there there's auto fact there's an injectors castle windsor unity there's a bunch of them out there in fact it's probably tradition that eventually when you get into this deep enough you build your own but to realize that you probably just want to use one of those so once you get that installed what we'll do is we'll register all of our dependencies with the container and then when we need something we'll resolve which is a fancy word in the ioc container world where we basically say hey go figure out all the things this depends on from top to bottom and then give me an instance back and from there we can actually use our instance okay we find ourselves back in code again so our objective this time is to use a dependency injection container rather than use poor-man's dependency injection so one of the first things I want to do is I want to kind of undo some of the poor man things and to do that we'll get rid of the default constructor and will be much more loosely coupled because now this has nothing to do with a particular concrete instance of anything and if we go to the Etsy the super Ness and we'll also get rid of it there too so we have no default constructors at this point if we go out here it's now complaining that oh no you now have to do the long form which would have been this here as a reminder let's run that just to make sure it works again there we go but now the goal is is to not have to do this as you see here where you have the new new new let's say these are very complicated so the first thing you want to do is select a DI container and for this example we're going to use Microsoft's unity and I've already pre-installed it by using the install package unity and it installs the next thing I want to do is create a container remember the container is gonna hold all of the known dependencies in the entire application so what I'll do is I'll do of our container equals new unity container and they these are different based on the frameworks so now that we have our container we're gonna register each of our dependencies so container dot register type we're gonna say anytime you need an eye game console I want you to use a Super Nintendo I want you to create one of those notice I'm just passing types there's no parameters nothing like that so that's where some of the magic is next I need to register an eye game so what game should we use with this system here well we're gonna use is Elda and then we know zelda depends on a weapon so whenever zelda says hand'd i weapon what should I use we're gonna go ahead and say well you're gonna use a sword and that will simplify greatly this down here in fact to the point where I don't need any of this so now I say hey container I need a Super Nintendo but I don't say Super Nintendo cuz we need to talk in abstractions at this point I need to say I need an eye game console and to do that we're gonna resolve it so we're gonna do it resolved and we're gonna say go get me an eye game console and what you'll get back depends completely on whatever you've registered here now oftentimes you'll move this code right here into its own registration class and there's at least three ways to be able to register your items I this is the explicit way or its configuration and coda says when you want one of those provide one of those when you want one of those provide one of those and so on and so forth the second way is to use reflection and do it by convention so we won't cover that here your di framework of choice documentation will cover that and then lastly you can register it through the app or the web config as a late binding option however a lot of people don't like that due to the possibility of typos and whatnot but that's totally an option for you okay now that we have a game console and it resolved here we should know that we should get a Super Nintendo because that's what we registered if we want it to be a Nintendo 64 or whatever as long as it implements the AI game console the container doesn't care and it'll just resolve it plus all of its dependencies so let's go ahead and run this and see what's going on hey it looks like we're back to where we want to be and it's resolving that so let's look into what could possibly go wrong well let's say I forgot to register a dependency here so we'll just comment out the sword they should throw an exception and what it's gonna tell us here is long story short I was trying to fill the eye weapon interface but it wasn't there so I'm gonna blow up so this is one of the major drawbacks in my opinion of a DI container if you don't register something it will compile it will build but it won't die until runtime so if we restore that there and run this actually will have to continue and then rerun it nevermind that that will resolve so next thing we can do is we can say hey let's let's do Super Mario World instead so let's run that and what do we get here jumping on Bowser's head how does that work well simple Super Mario World is over here in the I game world and here we go it's an AI game and it just resolves look at that it does not have any dependencies on anything so if we want to say you know what Mario now knows how to use a sword and we can do that by saying hey mr. Mario world you need an AI weapon your game is totally different in fact let's not use a sword let's use a hammer but we don't say a hammer right here because that wouldn't be very solid and it wouldn't it would be tightly coupled to that so what we're going to do is we're just going to do the weapon again and we're going to use reflection to give our boilerplate code there and then while we're playing the game we'll say attack and notice here we didn't say hammer or sword so what will he get because we have a ham over here and we have a sword here so it all depends on what we registered so if we come back out here the registration right now we should expect that he's using a sword so if we run this here swinging his Giants this Giants were jumping on Bowser's head okay awesome so we just somehow magically changed Super Mario World how it works because he uses a sword instead of a hammer but if we kind of want to return to back where you can use a hammer in mario world i weapon or hammer implements i weapon so this should work no problem and now we've swapped that out successfully that now we're smashing things with a hammer and jumping on Bowser's head so there's a pattern here to follow you should register resolve and then release I'm not sure if unity has a release option here but basically it's deregister the the particular thing that you resolved castle windsor you can resolve and then dispose of it some take care of it automatically for you unity might be one of those another thing is you should ask the question how many super Nintendos or Super Mario words or hammers do I use in the entire running of the whole programs to illustrate let's have game console 1 and game console 2 and when I run this we'll have game console 1 and we'll have game console 2 so just that quickly I should have two separate game consoles and you can see I am doing that there the question I'm really asking is how many hammers were created how many Super Mario worlds were created and how many super Nintendos were created was just one created of each one of these or did it reuse over and over so that concept is known as your your lifecycle or your lifestyle so depending on your framework of choice it might be singleton by default which means it'll just reuse if you don't tell me otherwise or it'll be transient meaning as soon as you're done using this and you need another one I'm going to new up a new one for you so you can influence that typically in your VI framework by doing something this so now we've explicitly said hey mr. Super Nintendo every time somebody wants one of you we're gonna new a new one up and it's not because of this new keyword here it's because of the transient so if we want to reuse the super mario world over and over and over there's a singleton and different life styles depending on your framework of choice so it's very important that you understand the the lifecycle of your dependencies because it really could cause you troubles with threading and things like that so that being said there's a lot of perils which we'll cover next so let's talk about some of the perils that come along with an ioc container in fact there's enough we're actually gonna have to read to you because I had to write down there's so many I don't want to miss anything so you need to make sure you register all your dependencies before you call resolve for the first time so meaning I can't ask somebody to create me an object or the container or the framework to say give me an object if I haven't registered all my dependencies makes sense next we need to make sure you release all of your constructed items that the framework has given you otherwise you might run into memory issues next be careful about your life cycles or your life styles depending on framework calls at different things because if you need single din or transient or there's about three other one per threads a popular one you can really tie yourselves in a knot you can have state problems threading you know all kinds of things so make sure you pay attention to what's going on there only use the resolve method in your composition route other otherwise you're kind of using it as a service locator and that's not what a DI container supposed to do in the case we'll say MVC or web api usually you have a controller factory and that's where you're going to go ahead and in register your container with the MVC framework or the Web API framework and it's going to know how to interact with your container next your will your framework of choice will have a ton of options make sure you RTFM and there's a ton of options that we didn't cover here so make sure you read the manual and then finally the big thing that gets me more times than that is the compiler can't help you during a build when everything is so dynamic because di containers use so much reflection to figure out what's in the the app domain that at compile time everything is gonna look okay and a lot of things tend to become missing because we forgot to register them or we register them improperly we had a typo if we used XML we use the wrong bit of reflection etc so a lot of times when you use a DI container things will fail at runtime a lot that would have normally been caught by a static compiler maybe Pete's right maybe this is not worth the effort so here's what my advice is to everybody so I think programming to solid principles are really really important and it gives you flexibility and allows for swap ability unit testing me personally I like poor-man's dependency injection on any project I start I don't want to immediately grab an IOC container because as you see there's a lot of trade-offs that you might have to make for all this cool functionality there's a lot of things that could go wrong and on a development team especially one that maybe not prepared to use dependency injection frameworks like an ioc container it can really challenge them and really give you possibly more trouble than it actually solves I just really want to quickly kind of call it one of my favorite books it's a dotnet dependency injection by Marc seaman it's a really awesome book it takes away a lot of the mystery around the ioc container side of things and even why do we do dependency injection in general so if you haven't read it before i highly highly highly recommend it I'll put a link probably somewhere under the video for you to be able to get to it hey guys thanks again for watching make sure you subscribe so we can keep doing this Thanks
Info
Channel: Kevin Giszewski
Views: 19,727
Rating: undefined out of 5
Keywords: di, dependency injection c#, c#, solid principles, dependency injection, interface c# dependency injection, patterns, computer, code, SOLID, dotnet, csharp, inversion of control, microsoft, to, c# ioc container, programming, coding, how
Id: xR0LotJQzlk
Channel Id: undefined
Length: 25min 17sec (1517 seconds)
Published: Fri Sep 22 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.