DEPENDENCY INJECTION in ASP.NET Core | Getting Started With ASP.NET Core Series

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
asp.net core provides a built-in dependency injection container the framework takes the responsibility of creating and managing the lifetime of the dependency objects in this video let's understand the default container that ships with asp.net core and the various features that it provides to make dependency injection easier so let's get started by creating a web application and see how dependency injection works in asp.net core heading off to my console using the.net cli let's create a new web api using the dotnet new command and specifying web api as the template this is going to create a web api application into the current folder to open this up in visual studio code let's use code and give dot to open the current folder this opens up visual studio code with the current folder loaded we see we have the program.cs and the startup.cs class in a previous video we learned how the configure method is used to configure middlewares in asp.net core if you want to know more about middlewares and how that works in asp.net core check out the video link here or in the description below dependency injection is configured using the configure services method in here you add all the services that your application requires let's look how this works before going into details of configure services let's scroll a bit up and see the startup class you can see that the startup class already takes in a dependency in this case it's an eye configuration so that's a configuration object which represents a key value pair now this configuration could be loaded from anywhere including your json files environment variables or azure keyword for example this dependency is injected in by the asp.net core runtime and there are a few limitations on the types that can be injected in here we can inject in a iweb host environment and also we can inject in our eye host environment which is basically the same as in the web host environment so if you're using a web host you would be injecting the web host environment you cannot inject in any other services through this startup class constructor if i was to ask for i logger this is going to throw an exception at runtime to run this application in visual studio code i'm pressing f5 which will prompt me to select the environment let's select.net co which creates a launch.json file so let's close that and press f5 again this is going to run the application we get an exception as expected it says unable to resolve a service for type i logger so we cannot specify any other interfaces other than the three that i mentioned that's i configuration iweb host and the ihost environment so let's remove the i logger to get this to work pressing f5 again should run the application as expected the application is up and running fine if we go to the slash weather forecast endpoint we get the api values back this is the default behavior of the template in the configure services method we already have one line of code which says services dot add controllers if you go into the definition of that you can see this by default registers all the dependencies that's required for the web api to work so this is adding in the mvc services that's commonly used for features with the controllers so features like api explorer authorization cores data annotations formatter mappings etc the asp.net runtime also injects in a lot of framework provided services you can see a list of them in the link here which will be in the description below so we can see the web host environment in here which we saw a while back we can also see there is an eye log of factory and i logger and also ni options and similar classes let's go back to the source code if we take a look at the weather forecast controller so i'm using control p in visual studio code to navigate to files so i can type in weather forecast controller you can also use a short form and specify wfc to select weather forecast controller so let's select that the weather forecast controller constructor expects in an eye logger dependency this is exactly how dependency injection works you state the need that you need a logger and you're not worried whether it logs to a file a console or to an external logging service all you need is a logger to which you can log to by default the asp.net code logger is configured to log to the console and a few other providers so if i was to write a log statement here let's say underscore logger dot log information and say hello from hell weather forecast let's run this application and we can see this logged in the console let's navigate to the weather forecast endpoint and you can see there is a hello from weather forecast in the console so if this logger was reconfigured to use an external logger let's say like a sql database or a file system these log statements would then appear in there with dependency injection we have inverted the dependencies the weather forecast controller no longer needs to know how the logger is implemented all it cares about is that it needs a functionality to log which it gets in through the ilogger interface let's go back to the startup.cs class the configure services takes in an iservice collection if you go into the definition of that it is just a collection of service descriptor so now what is a service descriptor a service descriptor describes a service with its service type implementation and lifetime the service type is usually the interface or an abstract based class it could also be a class in itself the implementation is the class that implements the abstract class or the interface or when it is a class it says the class itself again and also there is a lifetime at a high level there are three lifetimes in asp.net core transient scope and singleton transient is just like if you had written a new and called in the constructor explicitly anywhere you needed an external dependency so with transient the services are created each time they are requested for from the service container scoped lifetime services are created once in the entire request scope so within the context of a request if you ask for the same type twice you will get the same instance singleton services are created once when they are requested and then the same instance is used throughout the application lifetime so until the app shuts down you're going to get the same instance whenever you ask for a singleton service let's understand this a bit more better here i have a dependency injection container let's assume it is the asp.net core container and we have three kinds of services registered the t with the green marks are transient service sc in the orange marks the scope service and si in the yellow marks singleton service let's say we have a request coming in and to a controller endpoint which has two dependencies as shown in this image the controller has a dependency on a transient service imagine this would be defined like the i logger in our example in weather forecast so it gets injected a t1 which is an instance of type t if at the dependency that the controller has also has a dependency with t1 that gets injected a new instance which is represented here as t2 similarly if the controller needs an sc1 which is a scope service it gets injected a scope service 1 which is a new instance now if any other dependency within the controller needs the same dependency it's going to get the same instance so in here it will also be scoped 1. with singleton it's getting created the same instance so that's marked with s i without any numbering because that's only one instance now if anybody else needs the same instance it's going to get exactly the same instance let's say a new request comes in in that case with the transient it's going to get new instances represented here with t3 and t4 with scoped it's going to get a new instance as well for that particular request which is represented here with sc2 but note the other dependency also gets sc2 which is the same in this request lifecycle with singleton it's getting the same instance as the previous request this is what the pattern would be for these lifetimes let's see an example let's head back to visual studio code and open up the folder explorer let's create a new file you can either use ctrl alt n which is a shortcut for me or you could also use the button in here which should show you what the shortcut is for you so it says control alt n so using ctrl alt n it's going to create a new file let's create a new class called dependency dot cs i have some code written before so let me copy paste that we have an interface defined with i operation which has a guide operation id now i have different interfaces defined with eye operation transient eye operation scoped eye operation singleton and also i operation singleton instance all of these implements the same interface i operation all of them gets to expose the same property guide operation id you would have noticed by now all of them uses this lifetime scope names that we just saw transient scope and singleton so we will use these to register it into the container now the implementation is an operation class which implements all the interface and exposes a guide property operation id you can explicitly set this the guide by using the operation constructor or you can use the public constructor which by default uses a guide dot new guide so let's see how we can register this into the application so going back to startup.cs let's start registering them in the services class earlier we saw that the iservice collection is nothing but a collection of service descriptor so this list of services would ideally be taking in a service descriptor so let's start by creating one so let's say where item is equal to new service descriptor and pass in the values that it requires this requires a service type so let's first register the i operation transient interface so we specify the type of i operation transient the next it needs a function which takes in a service provider and gives back the object so that's basically a factory to create this object when it is requested for so let's define the factory by giving in a function and we just need to create a new operation class because that's all that's required for that particular type now it needs to specify a lifetime in this case we know we need a transient lifetime so let's specify transient to add this into the services collection we can use the services dot add method and pass in the item that we just created now this is a lot of code to write every time you want to register a dependency so the services collection has extension methods that helps you to do this very easily to do the exact same above we can use the services and specify the add transient method so this has various overloads we can use the generic overload in here so let's specify the angle bracket and specify the i operation transient and also specify the class that implements it in this case the operation and then let's close the angle bracket and the function so this is doing exactly the same as what we have done in here so we can now safely remove that and that's just one line of code to register this interface so to register the i operation scope we can use the services dot add scope extension method specify the i operation scope and specify the same class operation in this case now you know what the next one would be so add singleton to register the i operation singleton and then specify the operation class again for the i operation singleton instance we'll still use the add singleton but specify the i operation singleton instance in which case we can specify the object that's required to be created so this has an overload which takes in a function so let's specify that and give a new operation so if we want we can specify a specific goed in this case let's use guide dot empty so anytime this instance is going to be required it's going to return a guide dot empty let's go to the folder explorer and create a new file to create a dependency so let's again use ctrl alt n let's create dependency service 1 dot cs class i have the code returned before let's copy paste that so this is basically defining a dependency service one it takes in all the types that we just defined i operation transient scoped singleton and singleton instances it saves to this class instance and then writes it into the console when called the write method let's add the appropriate usings let's modify this from servers to say from dependency service one so we know this is written from the dependency service one let's also add a console.writeline a blank line so that it's neatly separated so this is going to write to the console anytime the right method is called when we can see all the values of the grid that's getting used this will help us to understand the scopes and how they work so let's make sure to format this and create one more class very similar to this which will be dependency servers 2. so let's copy this class and create a new class dependency service 2. so let's say dependency service 2 dot cs and paste the full condense there let's rename this to match 2 and also the constructor so we have two dependencies that takes in the exact same dependencies to that this is just for representation uses in your real application it might be different you might have different dependencies that depend on various other things and some of them might have common let's go back to the weather forecast controller and start using these dependencies so after the logger we can specify the dependency service 1 that we require and specify our name for that and also let's take in our i dependency service 2 and specify dependency service 2. in this case i'm injecting the class directly you could also abstract this as an abstract class or an interface and inject that in this is just to show you that you can also inject a class so let's add the two properties for them so we have the dependency service 1 and dependency servers 2 and these dependencies are getting saved to that let's come to the get method and start writing to the console so in this case we are not cared about this return so let's remove that and return the enumerable dot empty to make the compiler happy so let's call in the dependency service 1 dot write and also the dependency service 2 dot right so this is going to write out all the values of those guides that it has let's make sure to specify a weather forecast so that it knows what type of enumerable to return let's put a break point here so that we can debug this whenever it is running let's also go back to the startup class and add a breakpoint here now one thing that we are missing here is that we have not added the dependency service references into this services class so this cannot inject without that let's see what will happen if you run the application without specifying those dependencies it's going to show an exception that unable to resolve dependency service 1. so anytime the container is asked for a weather forecast controller it looks for the dependency service 1 and sees it's not registered because of which it's throwing the exception now so let's go back to the application and register that in to register the dependency service classes we can use services let's specify add transient because that is just like a new instance every time so let's specify dependency service 2 and specify the same class as its implementation so this is how you would register a class now to also register the dependency service 1 we can specify the dependency service 1 and dependency service point so this is going to register those two classes into the services container let's put a break point here also to see what is happening so let's run this application so it hits the services dot add controllers if you were to look at the services you can see there's already 69 count which means there's 69 items added to this particular collection and you can see all the service descriptors that's added along with their lifetime you can see singletons transient and all other lifetimes that's getting added also what kind of dependencies there are like host builder context i host environment i logger so all these are the framework injected dependencies when you call the services.ad controllers method it's going to call these kind of methods to add in the dependencies required for the web api controller infrastructure to work so let's step over and run this so once this method is going to get completed it's going to add all these services into this collection and you can see the count has now increased to 163. so let's run this and go to the weather forecast endpoint so this is hitting the get endpoint and calling on to the right so let's clear this console to understand what's happening a bit better let's step through the console so it says dependency service one dot right and then the two dot right so you can see this says from dependency service one and from dependency services two let's look at the values for the goods that's coming here so we can see that the transient value is different in each case so this transient starts with 345 and this one starts with 500 from the two dependencies for the scoped you can see it starts with 6e3 and it's exactly same as the scoped independency servers 2. like we saw earlier with a transient lifetime a new dependency is created every time it's requested from the container so when the dependency 1 asks for an instance of i scoped transient it gets a new instance and the dependency 2 asks it gets another instance whereas with scope it's tied to the request since all these is happening in the same request pipeline it gets an instance that's specific for that particular request pipeline so within the same request since both of them require it's getting exactly the same instance now with singleton it's the same in this case it has the same value in here note that in singleton instance since we over read it to use the guide dot empty it's showing a guide dot empty so let's continue the execution and make a request once more to see the second request cycle i still have the console from the previous request so let's step through this and see how this compares to both the request let me expand this a bit more so that we can see all the values that's written out so for the next request which starts from here we have the transient values again different as expected because any time it's requested it's going to create a new instance whereas with scope it's the same within this request cycle but this is different from the request cycle of the previous one so this one starts with 6e3 however this starts with an ffc with the singleton we get the same value as we got in the previous request so that is because the singleton is just created once for the entire app's lifetime with singleton instance we are still getting the good dot empty so that is working as expected so let's stop this and go back to the startup.cs if i was to add another instance of the same add singleton and specify the i operation singleton instance but this time not specify the guide dot empty so we are basically adding two registrations up for the same type the i operation singleton instance now in this case the last one always wins so whichever is registered the last takes the precedence and that gets injected in to see that let's run this application you can see now the singleton instance has a value non-empty good this is because we added the registration twice and the last one always wins now let's say in this case you are actually adding multiple instances for the same interface we want and i innumerable of those classes so let's come back to our controller class and ask in for an i enumerable of this class and see what happens let's add the all singleton instances in here which takes an i enumerable of i operation singleton instance and loop through each of them and write to the console let's run and see what happens so if i was to look at the all singleton references we can see two classes getting injected so this has two different types being injected into this particular variable since we asked for an i enumerable clear the console and loop through this so we see one of the values is the guide dot empty and the other one is the next value that we overrode that with so in this case when we added the service collection is still having all these types referenced there however the last one is getting served whenever you simply ask for an instance of i operation singleton instance which was what was happening in dependency service but when you ask an enumerable of that type you're going to get an enumerable of the instances for that particular type so if there was only one you would just get one item since in this case we added two we're getting two so let's go back to the startup class so let's say we want to try and add something so we want to check whether a type is first registered or not and then add that for those cases you can use the try add method so if i was used a try it's coming from a different namespace so we'll need to use that particular namespace adding that in should get this to work so using the try add singleton this is going to add only if a previous instance was not registered so now if we run this application we are going to get only one value into the enumerable because this one is not going to get added now we can see in the all singleton instances we just have one value this is because we used the try add extension method similar to try add singleton we also have the try add scoped triad transient there is also a try add enumerable which basically looks at the implementation type and ignores that if it's same for the register type so if you register the i operation singleton instance with the same new operation it's going to ignore that however if it's a different implementation let's say operation 1 and operation 2 in that case it will add that when you design services or classes for instance there are some recommendations from microsoft to make them compatible with dependency injection so basically it says design services to use the dependency injection to obtain their dependencies in these examples we used constructor injection which is the most popular used injection you could also define it using properties and also through functions but i always prefer to use the constructor based injection where possible it also gives in a lot more practices to follow through so you can go through that and see for yourself i'll put a link in the description below the default container that ships with asp.net core is for basic purposes if you have been used to other dependency injection containers like auto fact for example you might have seen it provides a lot more capabilities like using registrations by conventions now there's nothing stopping you from writing some custom code using reflection to add these dependencies into the default container that ships with asp.net co however if you want to get those out of the box you can also replace the dependency injection container that ships by default with one of these that's there in this list so you can go through this to understand how they work and use this to swap for a different container implementation but overall the concepts of the lifetime and how they work are mostly similar across all these different implementations i hope this helps you to understand more about dependency injection in asp.net core and how the default container works and also understand about the lifetimes and how they affect the objects and instances that you use in your application we have looked at dependency injection just from a container perspective but dependency injection is a much larger concept not limited to just using containers you could also use dependency injection without using containers if you want to learn more about dependency injection make sure to check out the book dependency injection principles practices and patterns which also uses dotnet core code samples this is an excellent book for understanding more about dependency injection and how to use it effectively to write loosely coupled code if you like this video please hit the like button i am adding in more videos on the asp.net core series where we will see different aspects of asp.net core and also good patterns that i have found useful when writing applications if you want to be notified when they come out make sure to hit the subscribe button thank you and see you soon
Info
Channel: Rahul Nath
Views: 13,240
Rating: undefined out of 5
Keywords: c#, dependency injection tutorial, dependency injection explained, c# dependency injection tutorial, c# dependency injection advantages, c# dependency injection .net core, dependency injection c# example for beginners, dependency injection container c#, asp.net core dependency injection controller, di in asp.net core, getting started with asp.net core, how does dependency injection work in .net core, dependency injection lifetime, dependency injection lifetimes in asp.net core
Id: YR6HkvNBpX4
Channel Id: undefined
Length: 25min 53sec (1553 seconds)
Published: Wed Aug 05 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.