C# ASP.NET 5 - More About Dependency Injection

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] [Music] we will switch to english now because uh we will now start the presentation and the today's lesson we have one hour and in this one hour today i would like to dive deeper into dependency injection and especially into certain topics how dependency injection is used to connect entity framework with asp.net core we have done many of the things that i'm going to show you today already in the exercises in the previous weeks but today i will go in much more details into how these things works how these things work behind the scene okay what i have prepared here is an empty asp.net core web application sorry asp.net 5 web application web api to be more specific and in this web api i would like to add a little bit of data access i've prepared the necessary classes we are going to use entity framework and then inject entity framework and as i said we will dive deeper into dependence injection so what i will do here is i will add a class and we'll call it data access as i said there is it's not absolutely necessary that you follow along in details today it's more about understanding the concept i will give you the my code after the lesson you see i have created a very very simple data model is it exists of a price class which contains a product description and a product price nothing special i wanted to keep it as simple as possible then i have a log class and this log class contains log entries and these log entries contain the username the date time stamp when was the log written and the description of the log we can write any kind of log here in order to make entity framework work with this project i'm going to add the necessary nougat package i'm not commenting that in any more details because you have learned about that and everybody should by now know that we have to add the entity framework sql server nougat package i will do that because i'm going to use sql server and also i'm going to add the design nougat package you have learned about that good here it is nice then we need to add a bunch of usings nothing special here okay using ant good now we have data access nothing special here pretty simple any questions about that no okay i didn't expect questions regarding that because it's really really simple the next thing that i would like to do is i would like to register in the startup method here in the start class in the configure services method i would like to add the necessary registration of our data context and i have prepared this one again i'm not going into any details because if you followed the lessons then you already know what i'm doing here i'm using the add db context here specifying the product context which is exactly the context that you see here very very simple and that's it last but not least we need to add the connection string to the app settings json let's do that here is the app settings json and again i've prepared that and we are done let's compile this guy good it works let's go into the terminal and let's create the migrations dot net ef migrations add initial migrations good ah we get a warning because in the data model i have a decimal type and we should specify in real world the database data type for this decimal but don't worry that's not that's not important for today that's perfectly fine we'll just ignore this this warning for today's exercise it's fine the the standard behavior the default behavior is perfectly fine for us dot net ef database update in order to write the database let me quickly check i think i have already prepared the database but i would like to show you to prove that it really works so i will delete the tables here let me do that oops sorry here drop table logs grop table prices drop table ef migrations history good tables are gone and now we can do a database update and this should recreate the entire database again a warning but it looks good let's refresh it here and you see here we have our prices table looks good here we have our logs table looks good and our migration history whatever that is that's an internal table of entity framework where it stores all the applied migrations you don't need to understand that in detail now it's just an internal system table of entity framework it's enough if you understand that good nice that was nothing special i just wanted to refresh your memory that whenever we add entity framework to an asp.net 5 application we have to register the database context inside the configure services method and what we do here is called dependency injection okay this is what this method is is called we are injecting the database context into the list of services which is called a service collection here you see it and whenever we create for instance a controller for the api we can ask for the data context because we injected the data context centrally in this location into dependency injection we have done that before that was just a refresh now we are diving a little bit deeper i would like to add a controller let's add a controller api controller empty we have done that before so again nothing special and i will call this the prices controller this is our prices controller in this prices controller i would like to add an http post endpoint anybody knows what is what is http post and why is poster important we had get post put patch delete what is post post is used for anybody an idea create exactly creating something right post is for creating something put patch is for updating delete is for deleting and get is for reading data and yeah in this case i'm going to use a post request um post is somewhat special here and we will talk about that in a second um let me quickly write the method signature here uh this is async async task of i action result we've talked about that last week price change and we get um a special a special class here i would like to build a controller with which i can do a general price change imagine that you are working in um in a store and the store owner just decides that all the prices should go down by five percent for equal for for instance or all the prices should go up by two percent or something like this this is what this method should really do and the parameters for the price change will come in through the hdp body therefore we need a so-called data transfer object the data transfer object is just a class which is used for as the name suggests data transfer it's just a class representing the input parameters for this method so we will do a public record record is very as very simple and convenient way to define a dto price change dto data transfer object okay string user and decimal percentage price change so we get in a parameter object this one and it contains the user who is initiating the price change and the percentage price change that we want okay price change now a small detail here i told you that post is for creating things inserting things but in this case we are changing something we are really applying a change to all the products in the database remember in the data access we had the price table where we have the product and the price and we want to iterate over all the products and change all the product prices according to this parameter here so why am i using post here and not for instance put or patch i told you that put and patch are the correct methods for updating things but now i'm using post well what we what we are having here is a very special case for an hdp web api and this very special case is just called an controller action it's it's like a remote procedure call it's like a stored procedure in a database it's not really updating a single record it's also not inserting a single record it's a more complicated larger operation with a name price change and it does various changes to the database it changes the prices but it will also create here in data access a new record in log so it's just a a more complex operation that we apply on the database and this operation consists of maybe inserts and updates and deletes and selects and whatever and for such operations again they are often referred to as controller actions we generally also use post so post is not just for creating a single record it's also for calling a procedure and whatever this procedure does internally imagine for instance you build a web api for a cloud computing provider you should provide a method with which somebody could restart a virtual machine in your data center which method put patch create delete you could say delete because it's rebooting that's not really delete post you're not creating a server or virtual machine so that that's not correct patch do you do an update of the virtual machine because you would like to reboot it no it's just a general action and for all of these things where it's not clear that we have an update and insert a delete for all of those cases you always use post so post is for creating and for calling controller actions and in our case it is just like this a controller action good this is the first thing that i would like to do currently i have not implemented this one not implemented exception so that we can go on in writing the necessary code additionally i would like to add another http post this one and this http post is just fill with demo data okay here we are really just creating records so post is somewhat natural because post is creating and we're just creating records we will just use this for filling up some demo data that's all now let's implement these two guys in order to implement these two guys we need the data context because otherwise we cannot access the database good let's do that we know already that we will add a constructor here and this constructor receives the data context the product product data product context let's store this guy this product context is made available to our controller because in the startup method we have added the database context to dependency injection that's not very new we have done that before but it's always important to understand how this works so we repeat that here you see adding the data context here you see the controller and because we have here the product context and here the product context dependency injection makes sure that the controller gets access to the data context okay good we will need that knowledge in a second because in a second we will dive deeper into how this dependency injection really works good let's add some demo data very very simple context.prices.ed new we will add a new price here and the price is for the product whatever um apples and the the price should be the product price should be i don't know 100. it doesn't matter data doesn't matter a weight context dot save changes async and return we want to add a status code created but we don't want to play around with the location header that we discussed last time so let's make our life simple status code there is a method which is called status code and here you can just say in http status code dot created this will not create a location header it will not return the the object it's just creating a 201 status code created that's all that's all we do here okay very very very simple in real life you should do whenever you create something you should do what we have done last week with all this um this this open api specification and creating the location header and returning the created object all what we have done last week applies today we want to focus on something else therefore i keep it very very simple but this is just because we are creating a prototype here okay good that looks nice i think we should try that let's run let's compile this guy good let's run this guy good and with that i would like to try it so i start here my visual studio code here it is and i will create come on request.http good and here we just say post https localhost 5001 api slash let's take a look the controller was called prices so we access it using prices whoops prices good the content type is json by the way i'm using a plugin here did i tell you about this plugin before i do so many presentations i sometimes forget that if i didn't the thing that i'm using here is the rest client i think we discussed that before right the rest client you can also use postman if you prefer postman that's perfectly fine and what should we add oh we don't need any parameters and we just want to say fill here because oh i forgot something here we have to say route fill in order to identify this method again and with that everything should be fine and we can hopefully run this and we will get back here you see 201 created worked nicely and if i say select star from prices let's see good apples 100 thumbs up this is what i wanted to do good now this product context here was filled by dependency injection now we would like to implement the price change method and now things are starting to get a little bit more complex so now comes the new part in this price change controller i don't want to add the business logic for adding for doing the price change and i don't want to add the business logic for writing something to the log i would like to factor that out i would like to put the business logic for the price change and the business logic for creating log entries into separate classes in our very very simple example here it would be perfectly fine to put everything into this single method because it's just a prototype and we want to get things done and that's it but in real world imagine if this is a larger application in real world the controllers and the data context would be overloaded by many many many many methods so you need a possibility to to structure your application and this is what we can do we can just create classes add class and put the business logic in there let's add a price manager class and the goal of this price manager is to contain all methods all business logic methods which are related for price management this could be i don't know calculating the price in a different currency or in our case doing price changes percentage-based price changes this is what the price manager should do he's responsible for all the business logic regarding prices let's build this price manager the price manager has a very simple method public void change prices decimal percentage price change this is what this price manager should do throw you not implement an exception in order to make it compile now the price manager obviously needs to access the database context because it needs to read all the products and it needs to update product by product so how do we get an an instance of the data context into our price manager well the answer is simple again we are using constructor injection exactly like we did in the controller so again just like in the controller we are taking the product context here this one and putting it here let's store this product context in a field here is and now we can easily implement the price change all we need to do is we need to make a for each loop and say var p in context dot prices and inside of the loop we just say p dot product price multiply equals percentage price change that's all we have to do that's the whole business logic the point that i want to make here is that we use this constructor injection not just in the controller here but we are using constructor injection in any class that is used for building our application now how do we get the product the price manager in our controller because here we need to call the price manager the answer is simple again constructor injection so what we do is we say hey dear dependency injection give us the price manager let's call him pm okay so we do exactly like with uh entity framework but this time with our own class price manager then we can very simple here say price manager change prices price change dot percentage price change and now we are calling the business logic from inside of our controller so we have nicely factored out we have nicely built a business logic class which collects all the relevant business logic for managing prices one thing is still missing because if you take a close look at this business logic it does the necessary changes but it does not store the changes to the database so this is typically something that you do outside so what you can do is you can say context.save changes async something like this i'll wait and we are good last thing return okay nice will this work answer no why let's run it okay server starts looks good but i told you it doesn't work let's see let's trigger a price change okay so let's do another post request prices and here we have two parameters let's check the first parameter in the price change dto is the user user that is foobar and the second one is the percentage price change i'm going to copy this property just to make sure that i don't mistype anything let's say we reduce prices by 10 percent let's call this guy boom we get an error this is what i expected unable to resolve service for type price manager the problem that dependency injection has here is you said in the control in the controller's constructor i need a price manager but if we take a look at the startup code here here in configure services nobody tells the startup method that there is a price manager this one price manager see that one so we need to register the class for dependency injection services dot add and now we have a problem because if i scroll down a little bit there are multiple options that we would have if i scroll here then relevant for our cases is add scoped add singleton and add transient we'll talk about these three now what is the difference add singleton the first one this one would make sure that there is only a single price manager a single instance of the price manager the highlander principle i don't know in your age do you still know the movie highlander do you still understand what i mean or is this movie so old that only old guys like me know what highlander is what about you do you know this movie is this something that is common knowledge never heard about it oh you should you should definitely look it up i'm pretty sure it is somewhere available i don't netflix or whatever highlander when i was young at your age highlander was a yeah it was a classic film okay you can't take a look at it it's it's really maybe strange because it's so old but it's funny singleton means that we only have a single instance and in highlander you had some heroes who were great sword fighters and they had to fight each other because at the end only a single highlander a single hero can can prevail and this is why many people and you will hear that that often in talks i think uh tell you something about the highlander principle because there is only one hero who can survive everybody else has to die and single means there can only be one only be one price manager and whoever asks for a price manager will always get another reference to the same instance of the price manager there will never be a second price manager that is single now what is transient transient is exactly the opposite transient means that whoever asks for a price manager will ask will get a new copy of the price manager so whenever somebody says give me a price manager asp.net will new upper price manager new price manager and give a new copy to the requester so you have many many many of these price managers and what does scoped mean this is the one we are primarily talking about today and it's not that easy to understand add scoped means that everybody gets a single instance of the object if the requestor is currently working on the same http request so if multiple a people ask for a price manager multiple people multiple classes ask for a price manager and they are all related to the same http request then they get the same price manager object but if another http request comes in and somebody else is asking for the price manager somebody else related to a different http request then this guy will get a new instance of the price manager that is add scoped so what is your guess for our price manager it will look like this new price new price manager something like this so what should be here transient singleton or scoped in our case what is your guess transient let's try transient transient would mean that we get a new price manager every time somebody asks us for we have to write it like that price manager something like this let's move it after the db context order is not that important but i think that's nice because the database context is added first and then the price manager because if we take a look at the price manager it receives the context is second okay add transient that's right let's run this one good and let's try our request 200 good and 90. nice works good so your answer was kind of correct why do i say kind of well be patient for another few minutes what about single if you would have said singleton would that work too you think so okay let's give it a try boom we had an exception and this exception was expected what does it say some services oops oops assuming some services are not able to be constructed cannot price manager cannot consume scoped something so no the correct answer would have been no it does not work with singleton but why the answer is this one add db context if we take a look at the price manager the price manager requires a database context so if there is a single price manager highlander principle if there is a single price manager there has also to be a single product context a single database context because the price manager needs a product context and if we have only one price manager there can only be one database context and by now until now we have never discussed in details how many db contexts we really have we just say hey give us a product context give us a db context because we want to access the database that's fine but how does asp.net do its magic does it create a single product context and give a single product context to us it doesn't matter which http request we get always a single product context obviously not and that is what we have seen now the point is that in this setup there is one database context for each http request so let me quickly draw that imagine this this here is an hdp request r1 let's call it just r1 okay http request1 then we have a second http request and let's call this guy r2 the first request comes in from the user and this request will get his own or its own database context so this one here this one is a database context okay just this this is a db context not very beautiful but i think you get the point and the second http request will also get a scoped db context here we get it's it gets it gets its own database context and if we have a third request running at the same time here the r3 guess what this http request gets again its own database context and this is meant yep yeah i know but you see me drawing right yeah whenever i draw the webcam is frozen that's that's normal and if i remove the drawing you will see me talking again okay but thank you for pointing that out so you see you get a database context for each request and this is meant with scoped this is scoped dependency injection so the database context is added in a scoped way and if we would now say with singleton that there is a single price manager across all the requests we have a problem because there is not a single database context it doesn't work so whenever we work with a database we need to add all the objects that consume the database context scoped because only then they can consume the scoped database context so scoped scope looks good and if we run this guy looks good let's go into our test call here let's call it looks good let's take a look in the database and we have another price whoops another price reduction by 10 nice now let's add let's make it a little bit more complex and let's add another uh manager this time we are adding the log manager class log manager and this log manager there will be very very similar to our price manager it has a constructor not hard to guess the constructor gets our product context context this context or this method contains uh this class contains a single method public void add log and we get the user and get the description and inside of here we just add this log so we say context.logs.add new log and here we say user equals user and description equals description and uh the last thing that we have is the log datetime equals datetime.utc now we set it to the current utc time universal time coordinates so this is the greenwich time nice very very simple so if we would like to consume this log manager inside of our prices controller what do we do that's a recap okay again constructor injection log manager lm store this guy in a field now we have a log manager and we can call log manager here log manager dot add log price change user and let's add um price prices changed by let's do it like this price change dot percentage price change multiplied by 100. price is changed by this is a log record and at the end we save it get the point we now have two business logic operations in two different classes so we have a way of structuring our business logic into classes and then we have a central safe changes async okay let's go to startup you will probably guess what i have to do now i will have to services.add scoped log manager see that one now we have the price manager and the log manager and now you see something interesting i hope it is interesting the price controller this one the prices controller gets the context you see but the prices controller also needs the log manager let's put it here okay the log manager also receives the product context see that one and third one we also have the price manager let's put it here so we have all these i have my screen is too small i think let's see if we can get it here i would like to draw a little bit here i think it i think it will work yeah i think it will work like that you see the prices controller needs the product context the log manager needs the product context the price manager needs the product context and they will all be used inside of this controller method so therefore now we have the situation that we have a single web request price change and in the context of this web request multiple classes demand a product context and scoped means that all of these three get the same instance of the product context as long as they work in the same http request if they are dealing with a separate web request they will get separate product context if they work in the same web request they get the same product context and i would like to debug that together with you so i can show you a bunch of tricks that you might not already know from debugging visual debugging c sharp code let's add a break point here in the price change method okay and we will also add a breakpoint here in the constructor of the prices controller additionally we go to the price manager and set a breakpoint here in the price manager constructor and let's go in the log manager and add a breakpoint here in the log manager now let's start debugging the web server comes up and you see no breakpoint was hit so currently no controller no asp.net controller has been created the prices controllers constructor was never called until now now let's go and fire up a web request let's call this web request boom with that we should have hit a breakpoint you see this is the price manager the price manager is the first object that has been created and the price manager receives a database context you see this one now comes an interesting feature and i'm not sure if i've ever shown you that feature before i think i have mentioned it but i'm not perfectly sure so i will repeat it what you can do is you can mark this instance with an id so what you can say is you can say make object id if i do that you will see here this dollar one one is just the id and whenever anywhere in the application you have a reference to the same object it will always have the tag dollar one okay so we can detect whether two product contexts that we have in our application are the same object or whether we have two objects of the same class okay so now we have dollar one this is the first http request let's continue running our application now we are in the constructor of the log manager and if we take a look here in the context you see and that's the magic of scoped because we have dependency injection using with the scoped with the ad scoped we now get the same same product context also in the log manager if i continue running it here we are now in the prices controller and guess what it gets the same product context because it has exactly the same scope the same http request so if you take a look at the price manager you see same context if we take a look at the log manager see same context so all these three now refer to the same product context and that is important that is really important because later on when we do the price change if you take a close look we are here manipulating database context through the price manager we are manipulating the database context through the log manager and finally in our controller we are calling the safe changes method it is important it is super important that all these three components of our solution operate on the same data context because all of them make changes to the database and they have to work on the same context otherwise bad things would happen otherwise the changes of the price manager would be down to one data context and changes to the log would be done to another data context and at the end the save changes would be done to a third data context and they they have no connection they wouldn't work together nothing would be written to the database because they work on completely different data contexts it's super important that we have scoped data context so let's run this through it's fine and now let's start a second http request again we are here at the price manager and if we take a look you see now we don't have the dollar one here we don't have the dollar one here because we in this case have a new http request we get a new scope and so we get a new context if i do uh where is it sorry if i here click on come on where why can't i see my make object id here it is make object id if i take a look now we have a new product context and it has the id 2. the same applies context is now 2 here context is now 2 and here my price manager has the context 2 my log manager has the context 2 and my context is also the context 2. so you see when you have another http request another scope is opened and dependency injection is done separately although we have multiple classes working together on a single http request this is the important thing here add transient works because a new instance is created whenever somebody asks for a log manager transient is more detailed than scoped and that's fine transient works with scoped but it's not efficient you don't need multiple log managers so scoped would be better scoped obviously works i've shown you that in the last few minutes but singleton doesn't work because if you add singleton then you have a conflict with the data context it's super important that we have a scoped data context questions so far now as a kind of closing before we start today's lesson i would like to enforce an error i would like to show you what would happen if for instance the database context would be transient okay what i do now is an experiment that you should never try don't try that at home bad things will happen it's just for demonstration purposes we are now doing a conscious mistake okay please don't forget that don't tell everybody mr stropak said you should do it like that no you should explicitly not do it like the thing like the kind of you should not do it like i will now do it this is the correct setup what we now do is we will break things i have prepared my own factory can remember we have created database context factories before so i'm creating here my own factory i hope you can remember it it's just a way of creating a database context manually so with this factory i am now able to remove this one this add db context and manually adding my product context through the factory so let's create the factory var factory equals to new product product uh context factory here it is and then we will say services dot ads transient this is wrong i told you again add transient new uh let's add this one factory dot create db context so we will just add a db context for each oh services ah we don't need the services we will add a new db context for each i did something wrong let me quickly no i did not i have to wait now we will add a new db context whenever somebody asks for a db context and now we will switch here to add transient ads transient 2. this is wrong again ok we just want to find out what's going to happen let's see if we still have our breakpoints here is the breakpoint yes log manager here is the breakpoint yes price manager here here is the breakpoint let's take a look in the database we currently have a price of 72.90 okay good let's run our app our broken app good let's send the request we will hit the break point price manager context you see it here let's mark this context with an object id this has object id1 let's go on log manager context oh no dollar one you see by adding transient i get a new product context every time i ask for the product context i don't have a central product context for the for the entire http request let's add another object id here and third one guess what new product context now we have three different product context our product context is the id3 the price manager's product context is the id 1 and the log manager's product context is the id2 so it seems that we have a product context but in reality we have three and now bad things will happen because the price change is now done on one of our product contexts the log manager uses a different product context and the save changes a thing async uses the third product context we don't get a mistake it's 200 here but guess what if i comp if i run that one nothing has changed we don't get a mistake but the database remains unchanged and why is that the case because our business logic classes and our own controller class they use separate data context and therefore everything is broken now could we fix this if we just add singleton here would that be possible well on the first view yes if i run a single http request it would work but bad things would would happen if multiple users would send multiple ad http requests at the same time because then different controllers different log managers different price managers they would all work on a single database context and therefore their rights and deletes and updates they would be a big ball of mess [Music] they would all operate on the same database context they would all do transactions and call safe changes and yeah a lot of bad things would happen so singleton is also wrong therefore let's delete the wrong ones let's make it like that and let's add you can remember it adds scoped here that is the correct option and then we can also get rid of our factory so that one looks pretty good if we compile this guy no no errors everything is good questions what should you take away from this lesson you should take away that you can build separate classes which contain business logic that belongs content-wise together we have a price manager for doing price related things we have a log manager for doing log related things and there are even more complex matters more complex ways of structuring your app with entity framework but this is a beginner course so we will keep things rather simple maybe in the second semester we will see how we progress so that structuring is important you can always when you structure it access the same product context and why is that the case because you have learned what scoped dependency injection is now you understand that scoped means you get a separate instance for each scope and that is in a web server a web request good this is what i wanted to demonstrate today and this is what i wanted you to remember i hope that was okay last chance for questions this was our last i would say regular lesson in this semester next week we have a lesson and definitely i will think about something um hopefully interesting hopefully interesting next week we will do a thing which is not relevant for your exams it will be just a fun and interesting experiment let's see we will do a little bit of cicd continuous integration and delivery with cloud computing and we'll take our asp.net stuff and put it into the cloud fully automatic i will show you a bunch of experiments i will show you how you can activate your your own cloud benefits that you get as a student so next week will be a a relaxed lesson where we do some hopefully interesting experiments but these experiments will not be part of your last exam so i hope we will have just just some hours of fun programming that will be the content of next week for today i would like to say thank you for the first semester thank you for working uh for working with you was really great thank you for that and yeah see you next week and enjoy the rest of the morning thank you for today and with that we close the lesson thank you goodbye
Info
Channel: Rainer Stropek
Views: 1,981
Rating: undefined out of 5
Keywords: CSharp, ASP.NET, ASP.NET Core, Dependency Injection, Entity Framework, HTL Leonding, Programming
Id: 2m9lV8-Yei4
Channel Id: undefined
Length: 54min 21sec (3261 seconds)
Published: Thu Jan 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.