Dependency Injection Setup - FULL STACK WPF (.NET CORE) MVVM #9

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
as bad as I want to keep pushing ahead with this application we're actually gonna slow it down a little bit and we're gonna build on our infrastructure by implementing a dependency injection container and one of the reasons for that is because we're starting to get a lot of services in our application as you can see when we wanted to test out our buy stock service we had to create an account service and to create the account service we had to create a dbcontext Factory then we had to create a stock price service and we had to create our buy stock service with all the services injected into the constructor right here or in other words passed into the constructor injected past and kind of the same terminology we're gonna be using when we talk about dependency injection but ideally what I want to do is I just want to ask for my buy stock service from somewhere and that somewhere is gonna be my dependency injection container and it'll give me the buy stock service with all of its dependencies passed in already so we don't have to do this manually and that's gonna give us two benefits so it's gonna allow us to ask for a service with all the dependencies already given to us and it's also going to allow us to define all of our services and dependencies in a dependency injection container which is going to be at the root of our application and it's going to keep all of our services and dependencies in one place so we can control our application and the services from that one place and we're gonna see that in just a little bit when we implement this dependency injection container so this app dot sam'l dot CS is really the root of our application it's where we start off our application it's where we show our window so everything about simple trader starts here and because of that we're actually going to implement our dependency injection container in this app class so we're going to create a method down here it's going to return an I service provider which is basically just a object that returns service is that anyone asks for and we're gonna call this create service provider and inside here we're going to create a service collection not to be confused with the service provider and we'll call the services and it's going to be a new service collection which we will import from Microsoft extensions dependency injection so dependency injection is actually built into dotnet core so you should probably use it so now that we have our service collection we need to register our services and we're just going to register these services and I'll show you how we resolve our buy stock service from the service provider so there's actually three ways that you can register a service you can add a singleton which basically means that the service that you register there will only be one per your application you can register as a transient where the service that you registered each time that someone asks for that service a new object is returned so you'll never have the same instance it's a different instance every single time and last but not least there's also ad scoped which means that you get a different instance of the service per scope and we can show how that works in just a little bit because you might be confused like what is a scope but for now all that we're gonna be doing is registering singleton because these are just services they don't have any state within them so it's okay if we share the instance of say an account data service or an ice or a stock price service throughout the application so to do this we specify the interface so a nice stock price service and then the implementation which is just going to be a stock price service so now every time we ask for a nice stock price service it'll give us a stock price service let's go ahead and do that for our other services at singleton so whenever we asked for an I did a service for accounts we want to get an account data service and then whenever we need a simple trader dbcontext factory we need to register that as well and for this you might notice we didn't actually have an interface here and that's because we don't really have an abstraction for this so if we look at this well I guess we could register this interface but that's not really ours that's pretty of any different record so we're just gonna register this without specifying an interface so basically whenever you ask the service provider for this you have to ask for this concrete type you can't ask for an interface and last but not least let's register our buy stock service and that's just gonna be our simple buy stock service and last but not least once you register all your services you have to build your service collection into a service provider and there we go nope and you got to return it okay so now we're gonna get rid of all this and I'm gonna say I'm gonna first create our service provider and then all I'm gonna do is I'm gonna get my buy stock service from my service provider and there's two ways of doing this you can get service or get required service the difference between these is get service will return null if your service hasn't been registered and get required service will throw an exception if your service hasn't been registered so I'm gonna actually use get required service because I just can't have null in this situation and we're gonna be asking for an I buy stock service and let's put a breakpoint here and make sure that we get our service so let's hover over this and we have our buy stock service and our dependency injection container injected all of the other services that are required so as you can see we got our stock price service we got our account service our account service has the dbcontext factory so everything is working as planned and all we had to do was ask our service provider for a service we don't have to know about any of these implementations so if we ever want to swap out any of these implementations it's not going to affect the rest of our application because the rest of our application pretty much everywhere we depend on interfaces so all we can do is just swap these whenever we need to and everything will work perfectly fine now you might be thinking ok great we have our service provider we can get services whenever we need them now all we need to do is pass our service provider throughout our application pass it into our main view model pass it into everywhere and then we can ask the service provider for services whenever we need to wherever we need them and that is actually exactly what we don't want to do that is known as the service locator pattern it's an anti-pattern and we're not going to be doing that because then your application has such a hard to pendency on the service provider and what we really want to do instead is we want to actually get our main view model from our service provider and if you think about it our main view model is really our entire application so if we get our main view model from the service provider then we basically resolve our application with all the services that we need and everything stems from the service provider in order for this to work though we're gonna have to register our main view model with our service provider so let's go ahead down here and then we're gonna add this main view model as scoped and the reason we're not making it a singleton is because the main view model has state it keeps track of things like the currently selected view model for the application so we don't want to register it as a singleton because we might have someone else who comes along and once a main view model that has different state so with that in place what we can do up here is instead of asking for a new main view model we can ask our service provider for a main view model and then since we've registered it as scoped if anyone wants a different instance of a main view model what they can do is go ahead and create a new scope so that's going to be service provider create scope and then they have their own scope and what they can do is ask for their own instance of a main view model and actually I'll show you guys how the main view model that we get from here is different than this view model so we'll put this into a variable and then we will compare our different view model to our window data context which is the other main view model that would create it so let's put a breakpoint right here and this should be false because they're different instances of main view models and there we go false so if anyone wants a different since of I mean be model I just got to create a new scope so we can get rid of all that we can get rid of our buy stock service resolution and now we are just resolving our main view model but for this to be effective we need to resolve the rest of our application when we ask for our main view model and that's not what we're doing so if we look at our main view model as you can see there's really nothing going on here and the reason that nothing is going on here is because we knew up a navigator up here but we really want our navigator to be instantiated by our service provider by our dependency injection container so what we're gonna do is pass the navigator through the constructor and then we're gonna set it in the constructor set this property and then what we need to do is register our navigator with our dependency injection container the interface type is going to be an eye navigator and the implementation is going to be just our simple navigator and we're doing this as scoped again because the navigator has state this is what actually keeps track of the selected view model so we don't want that to be a singleton we don't want that necessarily to be shared if anyone wants their own navigator they can just create a new scope so now if we put a breakpoint here we should successfully be passing our navigator through the constructor which we are but now what is this really doing for us let's take a look at our navigator and we have a current view model as a property that's okay but we have this update current view model command and inside here we create all of this stuff so ideally we want all of this to come from our dependency injection container we want our view models we want our major index service our major index listing viewmodel all this stuff should be registered with our dependency injection container and we should be getting it from there instead so we're gonna actually have to do some refactoring here and the first thing I see that I want to do is I want to create a factory method that will create view models for me and what we're gonna do after that is register that Factory with our dependency injection container and the factory is going to get a bunch of services sent down into it so what we need to do is let's create a new folder here and we'll call this factories so this going to be all of our view model factories and inside here we're just gonna create an interface and it'll be the I simple trader view model abstract Factory and what its gonna do is all its gonna do is return a view model and it'll be called create view model and it's going to take in a specific view type to create from and I'll give us back the correct view model depending on the view type and let's go ahead and implement one and you'll see why why we call it an abstract Factory and all that stuff so let's call this this simple trader view model abstract Factory and we'll make this public and it'll implement the interface that we just created and all that we're gonna do in this function is just execute this switch statement from our update current view model command so let's go ahead and copy this and so we'll paste it in there and instead of setting a current view model we're just gonna return view models instead so we'll return a home view model it it's home a portfolio view model if it's portfolio and if we get some kind of invalid view so that we're actually just gonna throw an argument exception and we'll say something like the view type does not have a u model and then the argument name where the perambulate now this still really isn't helping us because all of this stuff still isn't coming from our dependency injection container so what we're going to do is we're going to implement factories for each type of view model and then we're going to register those factories with our dependency injection container and that our abstract Factory is going to receive a factory for each of these view models and it'll just ask that factory for the specified view model and that's why it's called an abstract factory because it's going to take in a bunch of other factories so if that's a little bit confusing I can assure you it's all going to make sense once we start implementing these factories so to do that we're gonna create a new interface and this will just be called the I simple trader view model Factory it's not abstract and all that it's gonna do is first of all it's going to be generic and it's gonna be call it create view model so all that's going to do is create a view model of type T and we could even put a constraint on T so where T is view model base sudden forces all generic types have to be a view model so now that we have this interface what we can do is we can go ahead and implement a class called the home view model Factory and what this class is gonna do is just create view models for us or home view models for us so we'll call this the simple trainer view model factory for home view models so we'll implement this interface and all that's gonna do is return a new home view model and of course our home view model takes a major index listing view model so we're gonna need to create a factory for that as well so let's go ahead and do that so this will give you the major index listing view model Factory and again implement this interface and let's go ahead and return now we don't actually call the constructor for this we're gonna call our load major index view model and this takes a major index ServiceNow where we're going to get that from we're not going to new it up we're gonna actually pass it through the constructor so let's make a property for that and there we can generate a constructor make this read-only - and now why is this significant well we're gonna register this Factory with our dependency injection container so that means when we ask for this factory we're gonna get the major index service that we have specified in a dependency injection container so that means all of this is going to come from the dependency injection container and then another thing we're gonna do is passed that factory into our was it's gonna be a nice simple trader view model factory for major index listing view models just call this view model call this major index view model Factory and now we're gonna pass this through the constructor as well so that means when we register our home view model factory with the dependency injection container it's going to automatically give us the major index view model factory that we registered so now we can use that view model Factory to create the major index listing view model and now last but not least we're gonna go back to our abstract Factory and this is going to take an IPU model Factory for the home view model and we'll call this home view model Factory and it can be read-only because we're just gonna pass it through the constructor and now we can get rid of all of this and just call the home view model factory create view model and then we're gonna register this simple trailer view model abstract Factory in our dependency injection and it's going to give us the home view model Factory so now last but not least we have to do the same thing for our portfolio view model so we'll create a portfolio view model Factory make that public make it a whole lot of factories and all we're gonna do here is just return a new portfolio view model because we haven't even implemented that yet but this will just be a placeholder and then we're also going to inject a portfolio view model factory into our abstract Factory and then let's just create a new constructor whoops don't do that right I did okay let's get rid of the old one and there we go and then we have to also call that factory down here okay now some of you might be thinking this is nonsense creating all these factories why do you create factories why don't you just actually pass in your portfolio view model into your view model Factory and then just return it down here and the reason we're creating factories instead is because I want a different view model every time that I switch views so I can't actually just register the view model in our dependency injection container I need to actually register a factory that will create the view model when I need it I'm feeling pretty good about all of my factory implementations now I just need to register them in our dependency injection container so let's head back to our App dots and without CS and register all of these factories so all these are gonna be registered as Singleton's because they don't have any state associated with them all they really do is just create view models when we need them so it's okay if we share these instances and the first one we need to implement is the view model abstract Factory I say first thing but it actually doesn't matter the order that you register services so it's okay if we did this one first laughs it really doesn't matter and since actually just import this and then we can say we want the simple traitor view model abstract factory implementation for this service and you guys that might actually think that it's kind of crazy to create an interface for this but down the road I actually do have an idea where I want to have a different implementation of this abstract Factory so stay tuned for that it's gonna be fun and then we need to also register all of our individual view model factories so we have the home view model Factory and that's just going to be the concrete home view model Factory and then let's copy this for the other two view models we have so we also have portfolio view model and major index listing view model and then we also have factories for all of those as well and then also we need to register the major index service for this major index listing view model factories so let's register that that'll be a singleton as well and there we go so we should have everything registered that we need for our entire application but we actually need to implement this abstract Factory in our update current view model commands so let's go ahead and get rid of all of this and we need to actually get an instance of that abstract Factory so let's create a field up here and we can make this read-only I simple trader view model abstract Factory let's import that we'll just call this view model factory and let's generate a new constructor so we can get rid of this one generated constructor and let's go ahead and make this read-only as well and now all we need to do is say down here that the Navigator is selected or current remodel is going to equal the view model factories created view model for the view type that we get as a parameter for this execute method so that'll give us the correct view model set it on the navigator but now we need to pass this simple trader view model abstract factory into this update current view model commands and we're not actually going to register the update current view model commands because it's kind of funky how we create this so we have to pass in the navigator into this so in the end we could create a factory for the update current view model commands but I really don't want to I think I'm just gonna have the Navigator instantiate the update current view model commands for now at least if down the road we need a different update current view model commands and we can go ahead and register a factory with our dependency injection container and handle that case so our navigator is going to take in the I'm sick of typing this where it is I'm just gonna copy that put it in there so our navigator is going to take a view model factory into its constructor and then it's gonna set the update current view model commands to a new updated current view model commands pass in itself and also pass in the view model Factory and let's get rid of that and make it just a regular property and there we go so we have our Navigator registered with our dependency injection container so this view model Factory is going to get passed in automatically and now we should have everything in place for this to work so let's go ahead and we can actually just run the application and everything should be working as we expect and there we go everything still loads we can switch few models and it's perfect everything is now isolated all of our services are isolated in our service provider right here so the rest of our application doesn't actually know about these concrete implementations which is great so say that one day the financial modelling prep API just disappears forever and we can't use this stock price service anymore well if we found a different API to get stock information from we could implement a new stock price service and just register it right here and our application would still work so that is why that's really one of the greatest benefits of dependency injection containers is that you've registered all your services at the root of that of your application and you can just swap them out whenever you need to so last thing I actually want to do is let's go ahead and register our main window with our dependency injection containers so what we can do is let's go into this and we're going to change this constructor to take a data context in and then we're going to set the data context right here and one thing to note is that even though our data context is a main view model I'm not gonna put that right here because I don't want my window to actually explicitly know about me and view models if all I need is an object that's all I'm gonna ask for in this constructor so now we have that constructor and let's register our main window so down here we will add this as scoped again and it's gonna be a main window but we're gonna be doing something a little bit funky here so we need to register this with a main view model but we want to get our main view model from our dependency injection container so what we can actually do is inside this add scoped method there's actually an overload where you can pass in a funk or a function just like a little lambda expression and the parameter the input parameter for that function is the I service provider so what you can do is you can basically just use the service provider in this function as if it's already been built so we can say get required service main view model and when we call this it's going to give us the main view model that we register in our dependency injection container at the time that we ask for the main window and that is super powerful so now all we need to do is we can get rid of this and we can just resolve our main window and that's all we need to do to start our application and what is the issue I'm having here just in the other parentheses so now if I run this we should resolve our main window it should have a main view model and our application should be perfect and there we go so that's dependency injection in dotnet core WPF definitely something I recommend that people implement and it's gonna help us with the maintainability and infrastructure of our application anyways that's going to wrap it up for this part of the series if you guys enjoyed have any questions criticisms or other comments be sure to leave them below in the comment section but other than that if you learned something new or just simply enjoy the video be sure to leave a like or subscribe for more thank you
Info
Channel: SingletonSean
Views: 13,883
Rating: undefined out of 5
Keywords: wpf, mvvm, easy, hard, difficult, switch, views, microsoft, visual, studio, binding, viewmodel, property, class, interface, user, control, window, learn, how to, architecture, pattern, new, switching, toggle, button, content, main, programming, tutorial, full, stack, entity, framework, model, access, object, core, .net, c#, service, layer, project, finance, stock, trade, development, price, refactor, clean, update, delete, composition, delegate, join, navigation, injection, dependency, scoped, transient, singleton, resolve, factory, abstract, extension
Id: 3EzHn9ir5M8
Channel Id: undefined
Length: 31min 40sec (1900 seconds)
Published: Sat Feb 22 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.