Dependency Injection Explained with Lifetimes Singleton/Transient (C# Example)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome everyone today we're gonna be looking at dependency injection so if you find yourself in a category of people where you've been using dependency injection but you don't really know how works under the covers or you would like to know and you're basically no longer okay with just using it in your day to day life and being in the dark or you would just like to know a little bit more about reflection you came to the right place today we're gonna be building our own very simple dependency injection container and we're just gonna take a look at the inner working parts again starting real slow and in the same manner building it up step-by-step so usually what you do is you start with some kind of service I'm gonna go ahead and create a new hello service and by the way I'm using a link pad for this tutorial link is in the description it's not an affiliate link I am just very happy using this basic it to do my lessons so typical scenario we have some kind of service we might put something into this service before now this will be a super simple class where we'll just have a print function where we're just gonna do hello world and dump it all right and then on the service we can go ahead and call print right and the results are gonna be output here so I'm just gonna make sure I increase the the size of this so you all can see this so we have this hello service let's go ahead and make something that consumes this service all right so we have a consumer because new service can sumer right so service consumer is just another service so a service is just a class that's gonna be doing something and we what we're going to do is we're gonna have a constructor where we're gonna be supplying a hello service into our service that's going to be consuming a service right so here we have a hello let's go ahead and make a global field just lay up and now what we can do is we can grab this service we can put it into the constructor here right and and now what we can do on the consumer is go ahead and call print as well but what we're gonna do is we're just going to grab all our hello service and we're gonna call print on there and wait we don't really need to do much more it will just get two whole worlds and it's pretty self-explanatory what's happening here so what we're doing here is we're manually instantiating classes right so we're providing a little container of where the memory to basically have this class is gonna get allocated into right so we're saying we need to store somewhere this service somewhere in memory store it here etc right so this new keyword is the primary thing that we're trying to get rid of so what you will see in dotnet core is primarily right at the beginning in your startup you will register register or all the services that you were going to be using in your application right so this is what we want to get rid of this new keyword and primary way that we can get rid of is let's say that we will just get it will create a little container that's what a dependency injection container is we create a container where we register or all our services and that manages the creation so the new keyword so from this little application the reason I called it activator here at the top is what I actually want to do is I want to show you how we can activate these services ourselves okay and we can use the a the activator if you've seen it before you're going to be okay if you haven't seen it before this might be something new for you to learn so activator is a static class which is part of the reflection API and we can create an instance here and all we have to do is we have to provide a type of some class that we want to create okay and here we're gonna again we're just gonna I'm just going to comment this out here not that semicolon there here we have this service which isn't gonna be instantiating it in a different way and because what this returns is a object type we actually want to go ahead and cast it at the moment but really when we get a loop when we get to the dependency injection container we won't need to do any casting okay so we'll have this service and let's go ahead now and create the consumer so we provide the service consumer type and we want to create a service consumer okay so who are on this we're gonna run in into a exception no parameter parameter less constructor is defined right so because we have a constructor if we wouldn't have a constructor with a parameter we would be we would have no problem resolving this object this and we'll have no problem creating this object how we did with the hello service here we're failing here because it expects a hello services parameter right so what we do is with the activator we can then provide they're basically different overloads of this function we can provide a service as a parameter and now this will basically inject it so we're at the same point here but now what we're doing is we're using the reflection API to create this okay so this is just a one step in the direction of right we're not we're still kind of manually instantiating them but we're now doing it in a way where the code is instantiating them at runtime based on knowledge about itself right so what we want to do now is move in a direction where we can have a container where we store all of our types and a resolver which is gonna look at the container and it's gonna be like right here are the types that we have registered let's go ahead and try to use the activator to instantiate them right so you can see that the fight if we take a look at the cranes create instance function right here we have the type right and the way that you obtain the type is you wrap the type and type off and then you can get a bunch of information about that type so for example if I don't this again and the reason I'm explaining here if you're like why am i spending time on this some people don't understand this fully because they see it for the first time essentially what type of returns you is a type object so if we hover over type maybe not maybe if we store it in a and hover over a we can see it's a system dot type class and this type class is essentially code as data right so this is your code in its data representation and this is how it looks in memory and this is all the information about your code or about this class all right that we can create and what it essentially comprised of and what it is right is its definition and link that's really cool like this to let you look at it and all its different properties where we want to go from now here is essentially let's let me go ahead and save this one I'll call this resolver and container this is essentially what we're going to be building first steps is the container we want to be able to register the services just tell it how we do in dotnet core right so let's go ahead and create a public class and we will call this dependency container okay so what is this container gonna have well we're gonna have a list of types right so you know how we grab a type we just want to know about any type that we can create so who want to create a service consumer we're gonna need a hello service to supply it okay so we need to know about both types and we're gonna be storing these types in this list and we're gonna call these dependencies okay these are all dependencies and let's go ahead and create add dependency so it kind of looks a little bit more dotnet korish we're later are gonna go ahead and take a look at singleton and transient pattern lifetimes for these dependencies but oh nope it should be nothing to foreign if you've been doing C sharp sometime you're watching with this video about dependency injection I'm assuming you know something a little but something about a c-sharp so we're type you can take a look at this we're just passing any old value in here and we're just adding into this list right so this is just information about some type so like a class is a type a function can be a type etc but we're adding the dependency now we also want to be able to get the demand in dependency so we want to be able to return a type and get the dependency have trouble spelling dependency and we want to get some sort of a type what I'm gonna be primarily doing is for my dependencies I'm just gonna select the first one I'm just gonna expect to have it and I'm gonna compare by the name because essentially this is an object reference that's gonna be stored in here and an object reference that I'm gonna be passing in here it's gonna be two different objects so I can't compare the two objects I need to compare their names to really know that if it's the same type or not so now what I can do is I can register stuff with my dependency injection container so let's go ahead and create our container new dependency container and for my container I can go ahead and add a dependency and we can either supply it like this or we can even go ahead and add another cool little way of doing it is just supplying a type here and then we would have to say type opti okay and what this gives us then is maybe what you can call a little bit of a more aesthetically pleasing way of adding a dependency right so consumer service there we go and quickly if I would put a breakpoint here run this missing a semicolon that's alright object reference not satin and somewhat of an object always initialize your lists so we are gonna create a type here okay so we reach this a break point all what I really want I want to take a look at this container and really make sure that the if I look at the results for you you can see that I get the hello service and I get the service consumer and again I'm probably good come zoom in on the debugger sorry but essentially yeah we have the hello service and service consumer so we can use this or this way to register our types okay so it's just two different ways about having novelty right we register our dependencies right how about we actually go and resolve these dependencies right so I'm gonna delete actually we will probably need to look at this to kind of get an idea of what we need to write so let's go ahead and say dependency resolver and here in the constructor I'm just gonna go ahead and pass my container I'm gonna make a global field for my container I'm gonna make sure that I store it here okay so here I have my container let's go ahead and just be able to grab one service right so we have two services we have to do an injection with one of them what I want to do is essentially just resolve the hello world service because that is going to be a much easier thing to do somebody's gonna comment that one out for a second but what we essentially want to do is we want to get some sort of a type and we want to get a service right and of some type and in here we don't really need to pass a anything in the constructor again it's sort of going to be the same difference as we have here we supply the type that we want to add here which is gonna be supplying the type that we want a result an easy way that we're gonna do here is we're gonna get the type from our container so at this point we are just basically saying right do we have this type in our container so we're gonna get a dependency of T we can't really pass just T so we gotta say Oh tea and this is gonna get our type so this is a pass-off type and here we can use this service here you can use the activator to essentially cast to the type of this function that we're providing here and the type of we can just supply the type here okay so we're getting the type from our container and we're instantiating and casting it to the type that we are gonna be satisfying right so just making a little bit of space let's go ahead and create our resolver a dependency resolver in Dauphin encore think this is more or less called service provider or something like that so you have the I service container and I service provider I'm pretty sure the same object inherits from both of those and you're sort of depending on what which role it needs to fulfill well yeah we have the resolver let's go ahead and replace this part since we already have it down there translated to the resolver service we want to grab the resolver and we want to get a service of hello service okay and this a we don't really need it the consumer part we already know that we just supply the parameters stacked on so we don't really need to take a look at this as well so let's go ahead and run this and here you can see again we're just getting the humble hello world and this is now where we want to kick it up a notch we're gonna add the service consumer and we're gonna make sure that we're gonna check this this service need constructor parameters or does it not okay so let's go ahead and we already have that dependencies just that we actually want to resolve it right so let's go ahead and try to resolve service consumer still the same print function right no parameter parameter less construction defined right so we're in the same we're in the same basket as we've been before so let's actually bring it there real quick I'm just gonna return here so nothing else executes when I run this what I want to do is I want to show you how you can inspect and find things that you are looking for essentially because of them at the moment because at the moment we're essentially looking for a constructor so as I said before type off contains information about the type so we're gonna dump this we're gonna take a look at this list and we're gonna work you're gonna wait you were gonna do is you're gonna look through this whole list and you're gonna try to find something that represents any information about a constructor and what you're going to stumble upon is a declared constructor constructors properties right and then here you're gonna see that one constructor and when you look through this you're gonna be like hmm I don't see any constructors in its properties or fields that it has right there there's probably then a function that lets you get it because the results of functions don't get displayed here because you never trigger them so let's go ahead and actually get constructors because there can be multiple ones right we can get constructors and again these are the of type constructor infos and this is going to be a list so if this had multiple constructors we will have multiple constructors here okay so now what we can do is we can select so X is a particular constructor and for this X we can again we can just go ahead look through all the functions that are here and find something that resembles anything about parameters in the constructor because this is of type constructor info we just want to find all about the parameters on this constructor okay and we get a function called get parameters okay so here we execute it and you can see that we can see the parameter type type of hello service is on our service consumer and if we would have added something like string here and we rerun this we can see that we can see the name of the service and we can see the type of the service so primary thing that we're interested in is getting the type of the service ok that's how we know which other dependency to grab from the container in order to stick it in there right so we're essentially gonna be having a service which needs to exist before we resolve another service okay so if you watch the middleware video it's kind of the same where we need to know about the whole pipe before we about all the parts of the pipe before we construct the whole pipe okay so we need to know about the service that's gonna be going into that service before actually instantiating that service so yes what we want to do is instantiate this hello service type before creating the service consumer okay so logically what we need to do is we need to check if the dependency that we currently extracted right let's go ahead and rename this type to dependency and we will supply this dependency here again what we want to do is know which and rather not which if it only has one constructor we want to grab the constructor so we're gonna go to dependency we're gonna get constructors and what we're gonna do is we're gonna get a single constructor and what this will allow us to do is essentially check we can add more error checking around this topic or have it but essentially what this does is right you can only have one constructor because otherwise at runtime how are we meant to realize what constructor to resolve right so you can only get one constructor and single if we don't get a single response from this function it's gonna throw okay we make sure that we get a single constructor and then we are going to contructor conductor it's gonna be a constructor okay then we're gonna take our constructor and we're gonna get all the parameters okay so because we can have multiple parameters we want to instantiate every single service before we instantiate the next one and again I'm gonna be using recursion for this one our kerschen is very nice in this sense what I want to do is create a for loop we're parameters I'm gonna be going for each parameter this length and what I want to do is increment standard for loop nothing nothing too crazy now what I want to do is essentially store my implementations of those parameters the services that I'm gonna get gonna resolve using the activator I need to store them somewhere before I pass them into the final activator okay so let's go ahead and parameter implementations and this is going to be a new object array because once we initialize the parameters there they are going to be object they're essentially going to be instances we just don't know which type it is but that's all right parameters top length and we're gonna have as much as instances as much as we have parameters so that's why this makes sense so then we want to populate this right so let's go ahead because once we have the parameters its parameter info so and it's an array so what we can do is we can grab our activator it returns an object so it's okay if we store it there okay we're gonna create an instance and instead of the dependency let me go ahead and hide my output so we are gonna go into the parameters and we're gonna grab the same index that we're storing it into and we're just gonna get our type okay it's gonna be a function oh and I think actually it's not get type because I want yes soget type would have gotten me the type object for parameter info what I want is I want the parameter type so whatever parameter this is representing I want that parameters type okay so in our case the parameter type is going to be hello service okay and we're gonna create an instance of hello service we're gonna have any other services although we only have one so that is fine what we now want is we want to pass at ease but as you can see we would then lose the parameter less service so what we want to do is we want to put an if statement we want to check right do we have for our parameters length is it more than zero do we have parameters if we do then let's go ahead and do this whole thing with trying to resolve additional services and at the end here we can then do the same thing as we did here however we will also provide our parameters at the end okay and now let's go ahead and remove this bit at the top or actually what I'm gonna do is I'm gonna comment it out in case anybody needs it for whatever reason and I will get hello world again okay so what you can do you can put put breakpoints in here the break point I don't know here run this you will see that the dependency is serviced consumer the parameter implementations we can take it take a look at parameter implementations you can see that it's a Hello service because we take a look at the parameters we're gonna see that the parameter representation is of the hello service hello a parameter of the constructor of this one and in here we can see the parameter type is hello service the service that we're gonna try and trying to resolve and if we take a look do we have this yep container dependencies and again if we take a look at the dependencies we have registered hello service and that's why we can grab it okay so this is nice this is working for now because we only have two dependencies let's go ahead and add another layer and this is gonna really give it give it a test now let's go ahead and create a public class and we'll create a message service and it's gonna be doing the same kind of thing kind of print but instead let's go ahead and do message and we're gonna return instead of dumping it okay let's go ahead and turn Y all right and what we're gonna do is we're going to put this here in - hello service and let's we're putting message service in - hello service let's call it message and what I'm expecting what why am I doing this precisely is because to show you the way we that we activate use the activator here is gonna break instantly once we go past this second level so we're essentially doing here is we're making it and create concrete implementation of if we have parameters only go in we only expect that the parameters for the service are going to be parameter less okay what if our service that we're injecting here is also gonna have parameters it's gonna break right here okay so this is what I'm gonna show you how to essentially get around to and this is where I'm gonna be using recursion to essentially instantiate this service first then this service and then the next service okay and here for the message let's go ahead put it here and make sure that we output it not with a percentage thing rather with a dollar sign okay so you can see that it breaks right here so what we want to do here because here we're essentially resolving a specific type we want to resolve an object instead of the type right we can still do the casting but essentially this will be this is kind of the same same difference here but because we're voiding a returning void here we actually want to return an object here so for the get service we're gonna provide the type type okay and we're gonna move the whole thing that we have here down there okay let's go ahead and put this here type of a type of T we no longer need that let's go ahead put this here not the type class but rather the type variable the conversion we no longer need it because we return an object what we're going to do here is not a float we're gonna call get service type off T and we're gonna cast it to a type okay it's service yep and let's not forget the semicolon but now what this what essentially this allows us to do now to resolve this creation of instance because our parameter our the service that we're gonna try to resolve is either going to be parameter less or it's gonna have parameters and if it is if it does have parameters we want to again resolve a service until it has no parameters okay so what we're gonna do is we're gonna pass a get service and we're gonna get the parameter type that we're gonna supply into there okay semicolon here close this up run this so sequen contains no matching element this is essentially your not found in the depend not registered with the dependency injection container error let's go ahead grab our message service at it here and now we can add it like this one thing we do is we're getting user query the message service this is because on the message service I'm not actually calling the message function let's go ahead and run this and here we get hello world do all right double hello double the world so at this point we have more or less assembled a little dependency injection container and we have a dependency resolver as well where we can then use the resolver to get a specific service right and then call whatever function winning in this service and remember remember essentially the program needs to start somewhere so in the background this is what it's going to be using and instantiating what it's trying to alleviate from us is for us to be using the new keywords I'm not saying it's bad but you should be the only time you should be instead instantiating manually is for good reasons okay so let's go ahead and create another window care we're gonna be adding lifetimes okay so lifetimes for our services I'm gonna move this because it exists in the previous one and I'm gonna call this lifetimes okay so instead of just storing a type for the dependency I want to give it an object I wanted to know a little bit more about the object that we're registering so the service the dependency that we're registering I want to know about its lifetime let's create a ennum and we're gonna create a dependency lifetime and let's go ahead and run with singleton zero and transient is one okay so we have singleton and transient singleton is going to persist as we will see in a minute and transient is going to be a fresh instance every time so actually before we dive in I'm gonna show you how I'm gonna use to check this so here I'm going to create a power parameter list construction I'm gonna create a random and you will random I'm just gonna call next in the constructor right and [Music] I think they'll be alright let's go ahead and what is this this is going to be an integer I think yes so we have around the mintage er and we're going to be returning a message with this random integer okay so it's just important not to forget that this is what we're actually doing so what I'm gonna be doing here is I'm gonna resolve the service again I'll do it three times so we get a fresh instance every time yeah so let's do one two three same thing with this and three okay so we get a random number every time and essentially this means we get a random instance because the constructor is called every time a service needs to be created and that's when we set the random okay so we're trying to flatten it out we're trying to have the same number across all message services that are gonna be resolved because then we know that it's gonna be a singleton so for message service we're trying to register it as a singleton okay so what are the dependency the particular dependency that we're gonna and that's where I want to say that right this dependency also carry it's a lifetime okay so let's go ahead and create a class and we'll call it dependency we're gonna have a public type type yep so the type of service the type of dependency and we're gonna have a dependency lifetime okay so now when we add a dependency let's go ahead and say that we're gonna be using these types of functions because yeah they look better so instead of our dependency here we're going to be a singleton and here we're gonna say and transient what we're gonna be doing now is let's go ahead and create a constructor and I'm just gonna give these real stupid names don't do this I'm just doing it because I don't want to type the whole thing out and being lazy for the sake of time-saving we just want to add a new dependency every time we register it right dependency and we have the type and we when we add the singleton we can just say right this is what we actually call we just want to add a singleton right so dependency a lifetime let's go ahead and set it to singleton and the reason it's not compiling is because this is of type type go ahead and specify that this is of type dependency now and it's seen I have removed the element I should have done the initialization of the dependency list from the constructor rather than selling mistake from me I would have realized it sooner or later anyway so and the same same thing thing we want to do here so let me close that I will put this here and instead here we're just gonna say that this is transient okay and then now all we want to do is just reference the type here that we want to be grabbing and instead of type again we're gonna be returning a dependency so this will break some code up here and primarily what I want to do is get rid of this service consumer we want to add a low service here and both of these will add them as transient and the last one will add a singleton and I think I miss spelt it maybe that thing is Sigma I can't even spell single zone okay so we add transient and we add a singleton so the message service this is the part that's going to be will need to output the same number so essentially here you can see that we have different numbers we want to put the same number so this is where I create will be creating the implementations right so this is where I want to essentially say or check has this dependency Binker is this dependency a singleton if it is has it been created before if it if not let's create it and store it and then later on we can grab the same implementation and I will store the implementation on the dependency object as well right so just to make some of my life easier for me I'm gonna make it an object it's gonna be an implementation [Music] make a bowl of implemented to essentially make my life a little bit easier in terms of checking is it implemented or not okay so we're gonna go for the parameter parameter less construction first because it's easier and then later on we can change this as well so constructor what we're doing here is we're getting a dependency so what we need to do here is grab a dependency from a type so we're gonna be getting a constructor and by the way that this is not the correct and it's not necessarily correct implementation we can be doing different things here we can essentially once we register dependency we can also store the information about dependency whether it's a constructor or less or parameter or less or not or whatever once we register it and that saves us the time of doing it when we resolve probably gonna be a little bit more performant but nevertheless this is just an example of an explanation how this thing can work okay so we have our dependency type or rather the dependency class that contains the type and we still check of its parameterless constructor or not so it's not primarily we don't need to resolve anything else we can then go ahead and create this instance so we're gonna go ahead and create the type okay so this is where we want to check our dependency a little bit so our dependency we want to check if it's implemented already okay so if it is implemented we want to go go ahead grab our dependency and return the implementation right we if it's already implemented there is no need to re-implement it again it doesn't matter what lifetime it is so then we can check if it's not implemented we need to implement it because now what we can do is we can check the lifetime right so if the light lifetime is singleton alright we will need to store this so let's go ahead do our implementation blend meditation and for our dependency we're going to identify now so let's go ahead and create a public void add implementation it's going to be an object implementation and implementation a lot of a lot of implementations implemented istra okay so when I added the implementation I'm basically storing the implementation I'm just setting a flag that this is now implemented don't implement it again okay and obviously this again I'm just gonna reiterate because I'm thinking of all the different ways this can be done this is not the most correct implementation and again this is just a creative process of explaining all this can look like I'm just gonna as our implementation here right so has it been implemented before yes return the implementation is this a singleton if it is let's create the implementation and store it so then later on if we try to resolve it again we just get the cache essentially once we added the implementation we can actually go ahead and return it here as well or rather probably let's take this out here and return the implementation here okay too many letters in this word implementation okay so more or less looking at this I think this will work one thing that I'm thinking is will need to do will need to do the same kind of steps here essentially so what I'm gonna be doing is I'm gonna be taking this out into a function okay where I'm gonna be essentially creating the implementation this is what it's doing so from a dependency we want to resolve an implementation it's going to be an object that we're gonna be returning and remember the reason we were returning an object is because that's what the create instance function of the activator returns so create implementation provide the dependency okay so here we have the dependency let's go ahead and move all this code into here and we pretty much got our stuff here so one thing right now is this create instance we cannot sort of interchange it with this so what we want to do is pass a little factory so so the same way we can so we can basically define how we want to use the create instance function or how we want to create our dependency so I'm gonna class a function and that is going to accept the type that we want to create and that's going to return an object okay I'm just going to call this Factory okay and this Factory we're gonna go ahead and replace this here and I'm gonna be still passing with the dependency type so you know what this order looks like we're gonna essentially create the implementation too many words I can't cope with this okay so dependency and then the function so we're gonna have the type and I want to use the activator create instance and just do it for the type and then here I'm essentially I want to do the same thing right as it's doing just the type I'm gonna be providing here and the rest of the parameters well that's essentially the same thing I essentially made if you watched the little design the little that if you watch the middleware video what I essentially did is I made a little middleware here this is like many middleware functional programming essential okay so if you don't know much about functional programming to be able to do stuff like this to open up that kind of thinking I recommend you take a look at a programming language called closure but yeah this is essentially what's gonna happen now so let's go ahead and run this and what you will see is what we're getting is the same number for all of them okay and just to make sure that this is happening for a so that we are essentially we can define a singleton for our hello service as well let's go ahead and do that and this one really are reflected that much but what we can do is we can add the same random here and we can kind of edit or not so hello number and we can have the random here so the first random is gonna be from the hello service the second yo number is going to be from the message service right so you can see this these numbers are the same in this column in the same and the other column so if I change the hello service to transient you'll see these numbers start to change because the lifetime is different so we need get a new hello service every time that we run that we resolve the service constant consumer type so a new hello service is created for each of them but the same services injected into all of those different services okay so this is essentially like an implementation for a dependency injection container if you are essentially trying to map this knowledge to how it translates to model view controller so MVC how does that get it injected into those controllers and those actions that also uses a reflection but like on a bit of a different level because MVC itself is built using reflection in the same manner all middleware is built and our dependency injection is built if you want me to explain Model View controller in the same manner I explain these two I'll leave a comment right otherwise I'm not gonna exactly show you how this maps until I probably get to create that video but yeah not to ramble on too much yeah this will be it for this video if you enjoyed watching it leave a like subscribe if you have any questions make sure to leave them in the comment section don't forget to join the discord channel I'll be doing quite a few updates there I also have a giveaway coming up for the 10k celebration I'll be doing on my twitch stream most likely so don't forget to follow my twitch stream and yeah hopefully I'll see you around bye bye
Info
Channel: Raw Coding
Views: 23,125
Rating: undefined out of 5
Keywords: c#, asp.net core, middleware, tutorial, c# tutorial, asp.net core tutorial, dependency injection, explained, singleton, transient, how does dependency injection work, what is dependency injection, dependency injection lifetimes, lifetimes tutorial, asp.net core dependency injection, di, di tutorial, asp.net core di, c# dependency injection, dependency injection implementation
Id: NkTF_6IQPiY
Channel Id: undefined
Length: 43min 51sec (2631 seconds)
Published: Sun Apr 12 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.