C#/WPF - Dependency Injection and Async in constructors

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone welcome back uh got some requests from a few colleagues uh recently some stuff has come up in my day-to-day job uh around dependency injection how to do async inside of a Constructor and other sort of let's try that introduction again and other various things of that nature so I started poking around figuring I was like I'm sure I've done a video on this turns out I couldn't find one so here we are we're going to do one um few quick updates update are good updates are very good first gcon is happening yay gcon so if you're not familiar with it ah loud um this is get Kraken online developer conference it's completely free if you want to grab it they you can check out their live stuff uh days one and two are already complete today was day two so if you're watching the recording of this video uh you missed it um but day three starts tomorrow so if anyone is interested uh you can go to gon.com and you will grab the link there you can go to their YouTube page if you do register they um they do have some prizes drawing things that they are doing so huzzah if you would like to sign up um but that nothing stops you from just going and watching the stream on YouTube If you interested in my session day one like third or fourth one in I don't remember exactly which but it's there I promise okay uh on the note of that as well um we are going to once again pick up with the test project that I was poking around with uh last week specifically it leverages one of my net templates so we will be leveraging that uh if you are looking for information on some of the kind of the generic di stuff we're going to cover a little bit of it but this is going to be like di V2 we'll call it slightly more advanced level stuff because I think that's important to do um material design I want to make a quick comment on this as well so the uh material design in zaml the the tree view did merge that is in do not attempt to use it yet uh well I should say this the only way you're going to attempt to use it is if you um clone The Source uh unfortunately the Nate deploy key has expired uh the owner of the repo said he'd get me a new one this week so whenever that happens we will be pushing out uh a new nougat package with the 5 stuff I'm going to write docks we're going to see about getting the 5 release out the door I keep finding little breaking changes that I want to sneak in so I'm trying to I'm trying to balance how many Breaking changes I want to put in versus how many docks I want to ride on every breaking change so we'll see I thought about yanking the wiki in um and I might not I IID considered it because having the wiki as part of the repo has a nice perk in that people can do pull requests against it um it has the disadvantage of you give up all of the navigation bits and I'd have to redo every link and I'm like ah I don't know if I want to do that GitHub Pages might be better I don't know I don't know so for now it's going to stay as a Wiki because I couldn't make up my mind I went you know what pain versus benefit it seems too low so we're not going to do it right now okay so why do I have red red squiggles I'm going to restart digal Studio because I do not know what the heck it is doing and why I'm getting red squiggles I did just install a beta update because why not install a beta update right before streaming what could go wrong this is the problem with updates coming out on Tuesday and me streaming on Wednesday There's a high probability that I've just installed an update prior to streaming either that or I'd have to show restraint and and like not immediately take an update every time the little colorful Bell shows up in the corner I just love it so much okay what are you squawking about cannot implicitly okay something has clearly gone wrong and it would not Shock me to find that it is in the SDK but you know what I think it builds still so we're just going to ignore the red Squig and move on I think I just want to yeah rebuild all successful so yeah despite the fact that Visual Studio reports a compile a it builds so great okay so quick recap to to get everyone up to speed this is my WPF template that uses the generic host as a means of bootstrapping your WPF um specifically it uses this create host Builder which anybody who uh has gone through through like aspnet core or aspnet latest stuff net 6 net7 soon to be net 8 next month um will be somewhat familiar with uh it gives you a nice easy way of doing the configuration it also let you inside of your WPF app leverage anything that's built on top of the Microsoft dependencies abstractions there's a lot of stuff out there if you want to use a different di container any of that stuff any of the mainstream ones that are worth your time considering have Integrations here so it makes it a very nice way of doing the registrations it builds it up and the key line is this main window here which when you call get required service from an i service provider that is telling the DI container please create me one of these things in this case main window Now the default di container the Microsoft One does not have magic some di containers ERS do this reflection stuff well they'll scan the assemblies and they'll do various things the Microsoft one it's pretty simple if you don't tell it about it it doesn't know about it and it will throw up its hands and give up so in this case we've told that hey main window I expect it to be a Singleton right this is something called lifetime we'll get into more in a minute but Singleton means you get one of them think of it creating like a static instance the big difference between just using like the static Singleton pattern and going through something like this is now main window is ignorant of its lifetime it could be one it could be single it doesn't know it doesn't care and more importantly app doesn't necessarily know I mean it kind of does because of this but this could be often another th this method here could live in another class or similar but our main one doesn't necessarily know it just knows that in order to start this app it needs to get a main window needs to make it visible and Off to the Races we go um down in here th this gives us a lot of um flexibility but it doesn't really expose a lot of this and for my template I Tred to keep things simple and the beautiful thing about simple is they're easy understand the problem with simple is that they don't always have everything that's in there so let's go through and let's pick on a couple a couple common use cases so for example main window view model right this thing's very simple let's pick on something like I logger right uh main window view model log right and then we will we will magically go through and include the logger and then we will say logger I don't know assign there and then uh every time we increment we will do something like uh logger uh log information count is I don't know count and we're we're going to we'll we'll do some we'll do some Advanced logging stuff here we we'll we'll dive into it right so we'll we'll do the log message after we've done the increment yes we could do this inside of here that just makes it hard to read we're going to do it like this okay couple things I'm going to call out with the logger right away one you will note that I did not immediately do dollar sign and pass count in like this right instead of doing that because this would be straight string interpolation if we look at log uh information uh I don't have my zoom m in running I don't have Carnac running there we go get all the things going uh if we look at log information here we can see that the very first parameter oh it's so dumb it did that first parameter is a string which is fine right that's not that's not the worst thing in the world but if we do this and we look at it we see that the first parameter is still the string but we now have this pram AR that we're leveraging and the idea here is something called structured logging not all loggers support it but say you have something like Splunk something or or Siri loggers one of the very I I guess I'm mixing things there say you have one of the other loggers in place that wants to do some sort of intelligent logging because quite often what you want to do when you are hunting your logs is say find me all of the instances of the message count is right you don't necessarily care what this value is but you want to find all instances where this log information was called and you know that this count is going to be a variable with structured logging and an appropriate logging provider it gives you the ability to make those types of queries and then it can do fancy stuff around saying oh okay let's find where count is greater than five only show me those log messages because we've separated out the the value from the string now the logging provider knows that okay I've got a a variable here called count a value that's passed in called count and just to just to make a point int gets count right it's not matching on name this is positional so you could almost think of this as the equivalent of a string format where you do uh index zero but in this case we give it a name because names are useful and we should you should always give your parameter names that make sense and are indicative of what they're going to be so we will go ahead and do this log information and then I just want to prove a point so we're going to fire this up and of course it opens on the wrong monitor because that's what it does okay now when we click this button boom okay we can see that logger was appropriately populated right and we have the the type of this logger is microsoft. extensions. log. logger but the generic type is main uh main window view model right exactly what we specified here okay that's cool and nice we're going to go ahead and log count and then I think at this point uh we now have a little log message in our debug output right information level count is one huz so what what actually enabled this to work so something that is not in the template that I kind of secretly snuck in is I added in this Services uh ad logging here and this does a whole bunch of work for us which we'll we'll look at in a minute um but one of the the the thing that's worth being aware of is if you just do ad logging you get some defaults out of the box great We're Off to the Races if you look at the this overload here logging Builder we can do this and we can go logging Builder uh and we can come in and we can start to add in different loggers you can see there's add console add debug add event log um we can put in uh adding in different filters to be able to filter different um category logs we can do a Json console uh there's a lot of readers like adding Json console is common inside of like a a kubernetes thing where it's going to write out to the standard out but it's going to do it in a structured Json format that way any of those structured loggers that are watching standard out can pick it up which is a great interrupt way of doing it there's uh uh simple console system D console you can clear all the default providers you can set a minimum level all of this stuff right um that you that you can theor ically go through and configure if you want but what's most interesting here in my opinion is the fact that we registered ilogger of T we registered an ilogger of T so let's let's let's just play around with what what that ends up needing to look like right so let's imagine we had a public uh interface of an IU of T right and we wanted to come into main window view model and we wanted to do IU of main window view model right and we'll call this thing Fu we're just going to put a breakpoint here for purposes of looking at it now if we run this right now what should happen right is it's unable to resolve IU while attempting to create a main window view model great how did we get here if we look at Main window main Windows Constructor requires a main window view model if we look at Main window view models Constructor it takes in the ey logger and the IU our di container is not brilliant it literally is just walking through the constructors going what do I need do I know how to build that what do I need do I know how to build that that's it nothing fancier than that and it gets to IU and goes I have no idea what to do with an IU right especially an iue of T because normally what we would do is we would have an interface of IU right and maybe a class class Fu and then we might do a registration here of something like Services let's do add transient so we get a new instance every time and we'd go map IU and the implementation of iue is just going to be FU oh and Fu cannot be used because Fu actually has to implement IU in order for this to work great huzzah we're off to the races and now if we go to main window view model we can we should be able to resolve this guy now and have him magically work boom we actually got a foo instance AA that's what we like okay but now what if we want to do a foo of T right so this becomes who of T and this becomes F of T right we can do this type of registration here but how do we do ones with generics so the the thing to be aware of is ADD Singleton add transient all the the helper methods on I service collection these generic ones these are all helpers right if we take a if we take a quick Gander inside of here we can see that the add transient method does uh some checks and then comes in here goes add transient and comes in here and we can see that it calls add again and we get all the way down to this ad method and all it is doing is creating a service descriptor and adding it to the collection okay I service collection is literally a collection of service descriptors that's it and a service descriptor is not a very complicated class it has a service type and then it has uh one of several things so there's your service type there's your lifetime lifetime is the ones that you would expect Singleton scope transient ignore the fact that it doesn't have a default one that bothers me but whatever um so it had effectively it has a key and a lifetime and then one of these three guys is going to be is going to be done right either the service type will be mapped to an implementation type or it'll M be mapped to a specific instance or it'll be mapped to some sort of callback Delegate for a factory to be able to create it's going to be one of those right and that and that that's that's it it's it's no fancier than that at all right so if we go back what that means is I logging was able to add in something that was generic why can't we and so the way you end up doing this when you need to do uh generics is you can actually dang NAB you co-pilot see this is the problem with co-pilot it's spoiling the end jerk um there are overloads for add transient that do this but I don't I I actually don't want to do that right Serv service descriptor typing hard uh transient right I'm going to do this instead there co-pilot see and we're just going to call add directly so if you want to and if you're doing open generics I mean you have to you can register up something in your di container using an what is called an open generic so in C you cannot do you cannot do generic parameters which is what these things inside the little alligator braces are or for those of you who played Pac-Man Pac-Man braces if you want to specify a generic type and you don't care about the generic type parameters you can create what is called an open generic where you just omit them and the only time this kind of open generic syntax is allowed is inside of a type of trying to confirm if that's actually correct I don't know of a situation where you can do an open generic outside of a type of I think that might be the only instance and the idea here is this is going to return a system . type object that represents what a generic will be but the generic parameters are just going to be you know back tick zero it hasn't you haven't told it what type it is yet so this says okay for all instances of IU go ahead and create me a foo of te that's great that's what we want and now what we can do is we can come over to main window view model and we can go main window view model and if we launch this guy huzzah we now get a foo see if I can wish there was a way to say keep that open man I'm gonna have to I'm gonna have to set up a macro or something that zeros in on that really quick um but this will go through and do uh and create that open generic and now it will result solve it it's wonderful we're getting one step closer to how ilogger works so if we go if we go and take a look at add loging we can see that it goes through and does something very similar it does Services Triad uh which is similar to calling ad in that it'll first check if something's already registered and skip that way multiple calls don't end up duplicating and then it goes okay let's go ahead and register a service descriptor that's a a lifetime of Singleton and we're going to map the open generic of ey logger of T to logger great perfect that's what we expect but then we also see that there's this logger Factory it's like hang on something's a little off here right like at the end of the day this logger of T has to somehow do something with that t let let's take a look at what it does so if we look at loger of T we'll note that logger of T it's Constructor takes in an i logger Factory and this is a very clever trick the factory comes into the logger and the factory actually creates this ilogger instance so the base of I logger of T is in fact ilogger and ilogger just has a few simple methods on it for log message is enabled and begin scope and most of the time you don't use these methods directly you're going to use one of the extension methods on ilogger like what we did where you call something like log information you'll know this is an extension method here because most of the time you don't want to have to specify every little detail of the log message you just want to log a log message move on um and so inside of loger of t The ilogger Factory calls create logger and this T parameter ends up just being the category for the log messages so you can see it's doing create logger and it's passing this category name and now it's got this whole type helper where it tries to come up with a reasonable display name and apparently I can't navigate to the type because that would be useful and simple well let me go that way ha jokes on you I can still get there um where it's it's going to go through and basically use reflection on the type to figure out a reasonable string representation of the type because who knows it could be generic it could be an array it's not as simple as just type. name good for good reason it it should be slightly more advanced than that and it's got some some well-known dictionary stuff for the keywords to make those appear appropriate great huzzah we love that so in order for this to work when you put ilogger of t or in our case ey logger of main window view model what ends up happening is that because we have the open generic of ilogger map to logger of t uh in our di container it goes I got you in order to create the ilogger of main window view model I create a logger of main window view model okay and once again the DI container then looks at the Constructor and goes okay what do I need to create a logger I need a logger Factory great I already know where my loger factory is it was mapped from I loger factory maps to loger factory great I get my logger Factory I come in here and I say okay Factory create me a logger and use T main window view model figure out its display name and create me an i logger instance and now this logger of T implements I logger of T and ilogger derives from logger so this is one of those fun cas is where logari T both implements an interface and wraps an instance of its base interface which gets interesting right so down here when we log a message all it's doing is it's done explicit interface implementation because you'll note it has this ey logger before each of the method names and then it just forwards those calls into its wrapped logger uh implementation that it has this is a very clever way of using a factory pattern to and an open generic registration to make the consumption of it very trivial but then be able to still do some Factory stuff to make sure that the logger instance that's built up has all of the needed bits right it needs to get to the the various providers because it could be shelling out to that Json console the system D console one of whatever is in there it could be going to a file or whatnot so that is how you register up open generics inside of your di now next uh the the things that I've set up here either have a lifetime of transient or they have a lifetime of Singleton but there is another Choice as what we saw on that Eno right so we have Services add scoped well this is fun right so let's let's play with a scoped instance real quick and see what is what is the point of this inside of uh ASP net core the framework for ASP net core leverages what I am about to show you to create a scope every time a request comes into your service that's great it logically kind of makes sense to us each request should kind of be handled within its own trying to Define scope without using the word scope and it's scopey thingy in order for some level of isolation but inside of WPF WPF was invented long before this whole host Builder thing existed therefore it has no concept of this so if we want to use Scopes we can but the framework isn't going to help us WPF is going to give us no help we got to do it ourselves that's both it's blessing and it's curse WPF says do it yourself and we go fine fine this is why other libraries exist to handle the do-it-yourself parts uh okay so let's go into main window view model and let's do something like uh public interface I counter service and I don't know it's going to have in increment but you should spell increment correctly because that's important and then we will do a public class counter service I counter service and then we will implement it and it's going to be a a terribly complicated implementation uh private int count and I think this should just return uh count Plus+ there's a subtle there's a subtle fun thing here but we'll do it okay don't worry there's Mountain de in that cup uh let's see uh I counter service counter service all right so we'll go here and then down inside of this rather than doing count Plus+ we're going to do count gets counter service increment all right simple straightforward everyone's happy let's go back over to here we're going to we're going to go ahead and register this guy up uh Services add scoped I counter service counter service boom okay launch okay we click it doesn't work we click okay let's go fix that bug because it's going to annoy me real quick real quick it's going to really annoy me okay okayy spot the bug 3 2 one okay this is po increment not pre-increment so what happens is count is incremented but the original value is what gets returned if you do a pre-increment it will add one and return you the result one of the few times where this matters click there we go and we go to one immediately huzzah all right there we go so makes sense everyone's happy poof uh reset there we go okay this is great we've got we've clearly gotten oursel a counter service it clearly knows how to count not that impressive let's deal with this uh scoped thing so in order to leverage Scopes somewhere you need to pick up an i ser scope Factory uh let's see scope Factory that sounds great now this is something that you get for free by leveraging um the the generic host because you'll note I didn't have to register that anywhere inside of here this is one of the things that that comes in uh for free because you want to be able to manage those types of lifetimes just like I didn't have to do anything in order to have Singleton and transients work this scope thing comes in because in order for scope to work it has to exist now inside of here what I can do is I can go scope Factory uh create scope right and this returns bar scope which is this returns me a service scope now a service scope is a very very simple thing it's a an i disposable interface which means we should wrap it in using statement to clean it up and all it has on it is a service provider that's it right so if we come in here what we can do [Music] is one we'll we'll put this guy in a using statement and I'm going to I'm going to do this style just to be real clear on what the um scope of this scope is and now down in here rather than using counter service go and move these guys up I'm going to do scope dot service provider. get required Service uh I counter service we will go with VAR counter service okay and then we are going to rather than using our property we're going to use this instance of it so when we fire this guy up and yeah it's squawking at me saying hey dummy you could use the other using syntax you know what I don't care I don't care when I click the button I get a one when I click the button again I get a one I get a one I get a one I get a one okay what's going on let's take a look so if we come in here we click the button we can see we have a counter service and if we look inside of it we can see that the count on this guy is zero it currently doesn't have a count so when we increment it it goes to one and if we look at this counter service we can see okay it's it's got a count of one and we come down here we log it great and the next time we come in here we see the exact same thing we have a counter service with a count of zero because we haven't actually called increment yet we look at it again it's now incremented to one our count is always just being set to one the idea here is when you call create scope anything that you that was registered in your di container as scoped is going to uh the the service provider in inside that scope is going to be returning you new instances now this service provider here isn't limited to just items that were registered as scoped either because it basically gets a a child service provider might be the best way of saying it so for example we have a a a messenger all right so stop it we're going to go here let's do uh well actually we can pick up ilogger ilogger of main window view model right so let's grab let's grab that and look at a logger boom let's take a look at this guy we come in here we click the button we got ourselves a logger instance now more importantly if we come in here and do something like uh reference equals and I'm going to pick on lowercase logger and then I'm also going to pick on uppercase logger so the local variable here that we Rel that we retrieved from the service provider in our service scope and then the one that came in from our main Windows Constructor are in fact the exact same instance because ilogger was registered as a Singleton and it has the same generic type the same instance is retrieved when we ask for an iog or main window view model regardless of whether we're inside of a scope or whether we're inside of here so inside of WPF or anytime where you're using this you can leverage a server scope often times when there is a chunk of processing that you would like to do uh and you potentially want to do some clean up afterwards is often times where it doesn't really make sense to have well you either need isolation because of the the resources being used maybe it's EF with like a DB context that you want to have separate instances of uh maybe it's some sort of service that's holding on to some state that you want some instance of uh it because if everything works as a Singleton that's fine but sometimes you want slightly different things you could imagine in a WPF app um if you pop up a window that lets the user do some processing maybe you want to register that window and its dependencies is scope so that when you pop it each of those windows has its own scope the key thing here is you just have to request you have to one get yourself an i service scope Factory you have to create the scope that's the that's the important part and more importantly you have to dispose of the scope so you have to be in charge of the lifetime in aspet core they manage the lifetime to make it the life of the request and they just resolve everything from the scope service Factory that's where your controllers ultimately get instantiated from is uh an i service provider that's from a service scope that's how you end up getting those things and you don't have stuff stepping on all each other so it is up to you to decide what makes sense in a scope and what doesn't in general I start without Scopes and register everything transient or Singleton and then I will bring in a scope if there is something that makes sense for example I was working with a colleague he was basically building kind of this data pipeline stuff popping an item off a queue running it through a bunch of processing NE grab the next item rinse repeat right so what he ended up doing was creating a scope that wrapped the the processing of an item so that after he popped an item off the queue creates a scope resolves all of the uh needed services for processing that item inside of a scope does that work throws it away and Away you go and that way he didn't have to worry about uh any sort of kind of cross talk or data sharing between the processing of separate items again it's up to you your domain will dictate what makes sense the other thing that's worth noting about is if we do something like I don't know I disposable right this guy now needs to implement a dispose and I'm just going to uh I don't know count equals zero this is a really bad dispose method don't implement it like this in general you should Implement dispose by following so just to be clear the proper way to implement disposable is to come in here and use the auto code that says Implement interface with dis with dispose pattern this is the correct way to do it just to be clear because there are four different ways to implement I disposable depending on whether your class is sealed or not um let's see it's sealed or not and whether you have unmanaged resources or not those are the two variables and so you can imagine the four different kind of quadrants based upon the the result of those when in doubt let the generator do this for you implement the to-dos keep what you want keep what you don't want right we'll do it properly we we'll show the proper thing we don't have unmanaged resources or large Fields so we're going to get rid of this disposed of managed date yeah sure fine whatever right we'll do this and we are in a sealed class so it generated protected virtual if we were sealed it would end up generating private non virtual because it doesn't make sense otherwise okay and then you'll note it also handles having a disposed value so that it uh it's re-entering and all of that Jaz excellent now if we do this and launch if we go click step through steps through steps through you'll note now when the scope dies the scoped instance also gets disposed so this is another good reason why you might want to use the scoped instance is if you have a service that's disposable and you want to control when things get cleaned up again it it depends on if you have a logical bit for when to do the disposing uh let's see here and I have to deal with um fun people real quick standby soon as I remember how to get back to my uh let's see boom there there sorry sometimes you have to do your own moderation okay so but again key thing here if you have a scope service that is I disposable it will be cleaned up when the scope is cleaned up so controlling the lifetime of your services another very good common use case okay there's that there's that there's that there's that uh okay let's let's continue poking on this counter service since I've got it here let's imagine hypothetically speaking that for this counter service our our increment is more interesting and let's pretend like it's being saved off in a database somewhere right so our counter service is going to be persisting the count often uh off in a database uh and when we relaunch the app we want the initial value of count to be loaded from that database rather than just always defaulting to zero that way we can have some sublet of this actually you know what let's actually Implement that sure why not you know what because we can do this uh let's see await file file uh write all text uh count.txt this is bad code don't do this uh let's see uh okay boom there and then we're just going to make this task event task event increment async and we will change the interface I no go away stop it stop it stop it stop it stop it uh increment a sync there we go uh what is your problem uh cannot await a void write all text async there we go okay so and then let's imagine we have a public async task uh init async right so this guy here is going to go file uh read all text async because we want because the whole point of this is doing async stuff we're going to do count.txt right string contents uh and then we should probably await it because that was the whole point of doing that if uh contents is not null and in int. tripar contents out int uh starting value all right we'll go with count gets starting value okay perfect so now the problem comes how and when are we going to call initialize async the ideal situation here would be in the Constructor right we'd go init a sync everyone's happy and this kind of works so demonstration except for the fact that there are compile aers and it doesn't work uh increment async await async task boom there we go let's try this again okay we we increment we increment we increment yeah we we then crash because another thing is using it so Pro tip you shouldn't uh click too quickly but if we click again is that actually is that actually writing count oh yeah it's always writing a one duh we close and nobody's calling a knit a sync therefore or I guess it is calling a knit a sync so let's step into here contents reads a one trip parse contents count starts at one great now when we come over here when we do a click let create a new instance because we're inside of a scope count is one great comes in here increments to a two there we we go there we go so now we are now we are just stuck at twos constantly and back to one because we reset lovely okay so that scoped instance is kind of annoying but whatever uh but this gets back to the point now obviously there is a warning here because the call is not awaited right nobody is awaiting on this call this is technically a thing you can get away with this is bad just to be clear bad bad bad don't do this because what happens if this thing throws an exception because the task is not awaited where does that exception go it can't go back to the colar because as soon as you hit this await and this thing has to actually do something asynchronous it's now off on a different thread the first thread is finished the Constructor and has moved on so there's nowhere for the exception to go but up and out which likely means you're crashing right you're probably crashing at that point and because no one's waiting the task you might as well be async void again don't do this also bad right because async void is blind fire and forget what we would rather have instead is a way to say hey when we create this counter service let's initialize it but let's wait to move on until after we've actually been initialized because if we if we go fast enough The Constructor gets invoked as soon as we call uh get required service and if we go fast enough if we go fast enough for example if this thing takes a while task. delay I don't know a second right if we go fast enough uh and actually I'm going to I'm going to change this a little bit I'm going to stop using the service scope for the moment and instead we're going to use the uh the property so that we only get the that way we get the top level instance again this guy is still registered scoped and so that you always have your top level scope there's always the the default scope or the top level one right so if we click here you can see we skipped right over one and went straight to two right and if we reset this guy yeah reset resets all kinds of fun because clear clear and reset is um just doing this which isn't great right there's nothing nothing's going back to increment async so that's just going to keep counting but if because because the the first call went so fast the initialization was slow right so if we if we load this again Watch What Happens we're at zero we click right we jump immediately to seven we reset and then let's relaunch right so seven is what is saved inside of there if we click straight to eight how are we getting an eight I'm confused as to how we're getting that high this guy should be delayed long oh duh it's because this thing is created immediately the okay so hang on we're gonna because I'm not going quick enough time span from minutes there there darn it I'm going to make this thing take a while okay trying to prove a point and it's not helping my point okay if I click I now get a one and the reason I get a one is because I invoked an AIT async in my Constructor I did not await the task this guy's off going to load data you know pretend like this is a database call rather than just reading from a file but because because this is asynchronous and nobody's awaiting him no one has any idea if he's done he's just off doing stuff and then we come into increment a sync count hasn't actually been initialized and now we run into this problem because we go okay we'll just increment count count goes from zero to one and then at some point later this guy's going to finish I don't think we're going to wait 10 minutes and he's going to read the file come back and go ah finally we've got the initial value for count and we're going to throw away everything that we had previously incremented and move on right it's probably not going to be that bad because this right a sync is going to have the new value of one that's going to get written every time blah blah blah this guy will eventually be reading that file right it could be worse right the order of operations could look like this like there there's all kinds of ways we can make this go incredibly poorly but the question then comes is when you need to do some sort of initialization like this and we're going to we're going to dial this down just to something a little reasonable for Point purposes if you need to do some sort of initialize a sync rather than your Constructor what should you do right and there are a couple options you have available to you one is this this is bad another option is this this is horrendously bad in general you should avoid calling weight and result on a task pretty much you should do it never like bad bad bad the reason is because you've now taken something that was intended to be asynchronous you've now blocked it saying no no no my current thread is not going to move on until this thing completes a synchronously which means you're expecting that some other thread is going to go process this so while thread's processing the calling thread is going to be blocked sitting on its thumbs doing nothing waiting for that to finish worse if you end up with a situation where there ends up being kind of a a circular depy or a callback of some kind you can run into Deadlocks this is a common thing that I see happen with a lot of people who do weight and do result is they go huh we've got a lot of threads spinning up huh things are really slow yeah don't do this bad async going to wait all the way down right async once you start adding it to your code base it's like a virus it permutator getes up just like when we made increment a sync and we did it as a task I did it quickly but I had to come in here and change my increment count up to an async task this ends up being the top level method in my case because this is the command Handler but in general it's just going to keep propagating up so you can imagine four five 6 7even levels deep that asynchronous thing is going to just keep propagating and it annoys people and you should do it because it's worth it the moment you need an asynchronous thingy make your code asynchronous that is the way to do it now if in the very rare situations you have an asynchronous thingy and you must absolutely force it to to uh run synchronous I will refer you to Steven tab's blog posts there is an option available to you that involves task.run andwe and. result I am not going to show it because it is bad and people should never use it should rarely use it um but in the rare case where you actually need it because you know maybe you're interfacing with a third party or whatnot and you don't have control of something and it has to work that way fine if you have to do it go read that blog post um actually tell you what I'll do one better Stephen tab yeah believe it's this one let me just double check yeah now I'm gonna I'm gonna I'm gonna just draw your attention to one little thing here the size of my thumb on the scroll bar this is this is put forward as a blog post this is not a blog post this is a short novel okay at the end of this you will be very very familiar with how async and a weight works but this is absolutely amazing So for anybody who wants it um and then uh let's see sync over async is his other one uh this guy here yeah so there's this one and it's corollary post uh which apparently is I don't know why his link is dead inside the blog post but those ones there if you would like to to take a look at this before you do something crazy right before you start doing the task. Run craziness please read them it it is worthwhile okay now real solutions things that won't get you laughed at in a code review okay so uh a couple options available to you and this is there is going to be no silver bullets here everything here I'm going to couch with it's going to depend on what you're doing and what makes sense at your at your um implementation so option one option one you could in theory just do this uh let's see counter Service uh uh what oh I didn't add it to the interface hang on do that real quick so in it async right this this is a thing that can be done right so if we launch the if we launch this and we click you'll note I'm going to end up sitting here for about a minute I should have probably set the delay to be a lot less for this demonstration so I'm going to talk for a minute to stall for time if you are able one possible option is to simply just call the initialization code at appropriate points in your code so for example I was working in a project um we have various things that uh have something very similar to the dialogue host in the material design project and before a dialogue is shown it's very common for the dialogue to want to load some sort of data specifically asynchronous database web service whatever and so what I had done is I had created a base class for all of the dialogues a dialogue view model as it were and I put a uh an a sync method right on that view model and because all of our code for showing dialogues was flowing through a common code path it was very simple for me to say ah okay here's what we're going to do we are going to go through and before the dialogue is shown I will actually call that init a sync for you and then we'll go through and show it that if you have a code path like this looks stupid right saying well we'll just make the caller calling knit a sync at the right point it looks stupid but depending on your your structure and what you have set up there may be a reasonable point where you can where even though you know it's like I really want it in the Constructor no you don't you want it somewhere else trust me you want it in something that's asynchronous and it's possible to move it closer to the call site and say oh okay here's what we'll do we'll just do it at at time of the call site and put it through now another option available is to say okay rather than doing it there because you'll know this thing is now going to reinitialize every time I click this button that's real Annoying right that is that is probably not what you want right we could come in here let's let's act let's actually clean this up let's do something like uh private Bo you should spell Bo correctly uh is initialize thank thank you okay co-pilot write the code for me thank you thank you see copilot such a good helper okay so we could we could do something like this and now we don't pay the cost every time right and we're going to we're going to dial this sucker down from seconds 10 because I can I can stall for 10 seconds either than a whole minute 10 seconds is about how long you need for a drink a Mountain Dew okay so I click it we take the hit that's okay it's okay we can get over it we think for a moment we stall with talking we say something useful great it initialized we're now at three clicks right we can click again immediate because we're already initialized four clicks five clicks great reset we're still not passing the data through so we're at six whatever that's fine this is a possibility the other thing that's a possibility is something like init a sync move your initialization inside make your method async because often times trying to do something asynchronous at the Constructor is difficult especially if you aren't directly invoking your Constructor which with a DI container odds are your Constructor has zero references it's just not something you call directly because you said well I never called counter service I always just used I counter service The Constructor is this magical thing that just shows up for me great fine making the me the the methods act on the interface async and then you don't even need this right we can get we can get away with this and we can come down here and go oh okay rather than doing it in the Constructor because the Constructor shouldn't be doing this logic anyway the Constructor for a type is designed to uh construct the object um into a valid State and some people go well until I've read this starting value it's not in a valid State it's not really it seems like it's in a very stable state to me even though count hasn't been initialized there's no way to call increment without initialize being done and this is not now all encapsulated inside of my type so this ends up working rather well okay real quick uh task clear async we're going to do this because this is bothering me that we aren't properly handling this and we're just going to keep going uh let's see await uh file uh write all text async we're going to write empty string no we are going to write to uh count. text that file name should really be a constant or something and we're just going to do an empty string and call it a day there and we're just going to come down here uh async task that is not how you type async task let's try that again and then we're going to go with uh counter service clear a sync and we should probably await that so that I don't look as dumb as I feel okay so now at least now at least when we clear it and click the button we aren't going to get those weird increment things just going to test real quick to make sure click thinking thinking thinking while it initializes stalling stalling stalling for time taking a do boom okay if I reset and then click reset oh reset isn't actually so it turns out you should probably do that otherwise reset isn't going to do a darn thing we wrote the data to the file and then proceeded to not change our internal State this is why you write tests because unit tests wouldn't have been that stupid or maybe they would have been okay if we reset now there we go now we're at least behaving like a real boy okay great huzzah okay so options one create an initialize async method and either invoke it at the call site or relevant starting points or move it inside of your class and invoke it from the various members that might need to initialize it so for example this guy here might also need a weight in nit a sync right obviously that's kind of stupid but it would make sense that all members in here might need to verify initialization has been done right simple straightforward that's all it's going to take okay other options other options available to us uh another one that we that we can theoretically Leverage is we could leverage I hosted service okay so IH hosted service is once again something that you are going to be picking up by leveraging um generic host stuff uh and this guy here is coming from the Microsoft extensions hosting so if you do not see it show if you don't see IH hosted service showing up confirm that you have microsoft. extensions. hosting enabled okay the idea with a hosted service is it is something that you want your di container to spin up for you as part of the the lifetime of the host right so we're using the generic host a hosted service is a service to serve the host so when you have a hosted service when the host starts up will call start a sync on your hosted service when the host shuts down such as when you're closing down your app it will call stop async right so return for now we're going too return task complete a task right Bippity bopy boo y the advantage of this is you could theoretically do await uh init async and because this is an asynchronous method it gives you a nice easy way to go through and do this right so I'm going to I'm going to comment this out for the moment comment this out and I'm going to comment this out and then in order to actually have this work you can't just add the interface you actually also have to register it so if we come over here and we go services. add hosted service we can do counter service now this doesn't work okay this doesn't work uh let me demonstrate why so we're going to do a breakpoint in stard a sync we're going to do a breako at the end of stard a sync and we're going to do a breakpoint in an an increment async and so we'll we'll catch it we'll catch it in those three places okay so our app is starting up we immediately hit our breako at start async great we hit the Go Button we're waiting we're waiting we're waiting for initialization okay we finally finished my window isn't up yet just to be clear right we we've added to our startup time so we go there now we have this now we have a hosted service now I click the button my count is zero right so I go through I write a sync great do this write a sync right this seems like it's working I'm going to close the app I'm going to launch it again we catch stard ayc going to let it go we're going to question why my cup is empty and grab another Mountain Dew while we're waiting Gra great that was 10 seconds we've finished initialization great we're going to come over here we're going to click the button our count is zero okay it's not working you might go hang on hang on something's off here right I just saw start a sync it called it called a knit a sync what's going on here well let's examine let's restart one more time so we know our file has a two in it by the way okay so we get into start async and I would like to go to my let's do my locals window okay so I have a test counter service I'm going to right click on this and hit make object ID so you'll note I don't know if if people have seen this in Visual Studio when you hit make object object ID it gives you an ID by which you can reference this object in the the watch window IM immediates whatever right you now have basically this nice little static identifier that you can use to go and and get at this instance of counter service it's also really handy because you can use it to compare if two things are the same instance spoiler we'll let that thing go for a moment take the opportunity for a du question why I haven't made my task. delay shorter than 10 seconds okay so we finish our start async our window launches were good we click our button we get in here let's go to our locals and the first thing I'm going to note is that this test counter service doesn't have the dollar sign one next to it it's not the object it's not the same instance if we hit make object ID we'll note now oh uhoh we've got both a dollar sign one and a dollar sign two and if we look at these guys we can see dollar sign one has had its count properly initialized to two but dollar sign two has not been initialized you'll know is initialized false oh something's wrong here right so when you do ad hosted service let's take a look at what this guy does it goes through and it does a try add a numerable meaning it's going to attempt to add multiple I hosted services this IH hosted service thing is expected to have more than one thing hence the innumerable right you can think of it as a list with this is the key and you can see it creates a service descriptor of a Singleton of I hosted service hey Andrew I I hope it's a fun a fun topic a couple of my colleagues inspired it so they uh they unfortunately get get to be milked for Content now um and they shall remain nameless unless they're on in which case then I'm then I call them out by name crap I've forgotten what their twitch handles are this is where you see people closing the windows um so hosted service is going through and trying to register it here now this is problematic right because we are no longer in control of this service descriptor that it's creating because remember when we talked earlier about a service descriptor the service descriptor has uh a few things on it a service type or a key type right think of this as your interface type in most cases right a lifetime and then one of these three implementation things tacked on right right in this particular case it's going to be implementation type that gets specified because it's using the generic method here right this generic method says build me up one of these things that's of t- service T implementation and the given Lifetime right nothing fancy here so we've got service type implementation type specified lifetime of Singleton but that means that you've got two service descriptors now registered inside of our di container because remember I service collection is not fancy list of service descriptors that's it no fancier than that is uh small Aster I'm only mildly lying um it's functionally as a list you'll be fine so we've added two instances of counter service both keyed slightly differently now the work around here right the work around here is very similar to what we did down here right so here we registered up a Singleton of weak reference messenger and then we did an add Singleton where we reach back into the service provider and grab that weak reference messenger out and register it with another service descriptor keyed on IM Messenger right so these two each add a service descriptor this service descriptor has the service type and the implementation type specified of weak reference manager this one this ad Singleton adds a service descriptor with a service type of IM messenger uh and an implementation Factory that returns a weak reference messenger and that factory method recursively calls back into our di container and says hey by the way di container yeah give me that weak reference messenger that I that I registered already and so this one ends up resolving to this service descriptor pulls that instance back out so now it doesn't matter if what is requested is a weak reference messenger or an IM messenger both are going to get the same exact instance we need to do the same thing here right so we want to go here and go provider uh and then you should Spell correctly because that's important get required service and then we will do uh counter service and actually this doesn't quite work either because we haven't registered anything with a service type of counter service so let's do uh you know what I'm comfortable doing this so we could either do yet one more registration and recursively call each of them but because we implemented I hosted service on counter service directly rather than on the interface you can't have the factory return something that doesn't Implement I hosted service but in this case because the implementations are right next to each other and we're just going to hard cast because we know how that's going to resolve we could do this same pattern again and forward this one into that and this one into that and blah blah blah blah blah yeah okay fire this guy up hang on in the interest of me not being annoyed we're going to just turn that down from a 10 to a three okay so we start wait 3 seconds one two three boom we hit our second break point great we come over here we hit click me and you'll note now count is two and is initialized is true we've gotten the same instance in both cases I should have probably done the make object ID thing again just to prove my point uh so we'll go locals make object ID so there's our dollar sign one got our first reference come back over here we'll click and now when we look at our locals we'll note that this has the same uh dollar sign one as our other one so the same instance that we got for start async is now the same instance that we are calling increment async on so now everything goes back to working just fine so uh benefits and drawbacks of this this approach this works well if you if you are okay with one you're generic host managing the the startup of it and you can do whatever you want inide of the start async the the documentation for I hosted service we'll even show you how to go through and actually maybe I'll just pull it up there is create a timer service so in the event that you don't want it to just uh do one instance of initialize like for example you want a recursive thing that's just doing do work do work do work right you can and I will share link um you can go through and do you can go through and register up a timer in your start async for example and have it start incrementing so you can have your hosted service doing work in the background right and being able to go through and and build it up there is also a background service class so in the event that you really do want that um that background processing there is a very helpful Base Class you can derive from background service and then all you have to implement is execute a sync and then once again there's a cancellation token all that stuff it deals with everything for you you just focus on this and the background stuff just works great huzzah uh let's see question from Justin bber who calls start async zero references ah very good eye very good eye so start a sync in here uh so one it's coming in from IH hosted service and because we are using the generic host the host is what calls it so hosted services are started by the host and stopped by the host so start async calls when the host starts stop async calls when the host is shut down so and I realize there's actually a bit of a bug here huh I am never actually stopping the host I I I mean I kind of am in the fact that this using is going to trigger as soon as we exit main or on our way out of Maine so it might not be as big of a deal I'm curious now hang on I want to see if stop acing actually gets a chance to be called I have a sinking suspicion that stop Ayn may not get invoked let's look and that would be an artifact of my template and how it spins up the Generico so I shut down yeah interesting I'll have to fix that I'll have to fix that because we're starting the host here but we're never actually stopping it and it should probably there should probably be a registration here for app shutdown uh or what is it on shut down shutting down there there's an event here for this uh exit that so there should probably be something akin to to the to this here where it where it ends up grabbing it and shutting it down but then you also have the the async nature to deal with so details we'll handle that uh that's probably something I will put in my template once I get it in but yes so hosted services are un option available to you and again the whole point here is you have some sort of initialization we don't want to do again this bad please don't do this bad and again there's the there's the weight and the result and all of those are bad options and there's the trick with task.run but please don't do that until after you've read Steven tab's blog post okay that that's just the rule if you're going to use that you have to read his blog post first if you make it through the blog post blog post the the short novel then then you are allowed to use it that is roughly the rule I would give people must read first then you may do so but hosted services do give you a potential option if what you need to do is kind of onetime level initialization or background initialization and you aren't as picky about when that gets invoked it can be an option for you right again weigh that against doing the invoc at the call site because those are kind of functionally the choices available um to you let me make sure I'm not missing anything uhm yes okay so last option last option and we I kind of spoiled it earlier with the the logger stuff but that was kind of the point in showing the logger stuff see method to the madness I actually kind of like mentally thought through the order I wanted to show things tonight it's weird bit of planning I did not used to this normally I just wing it um okay so the other option and it it kind of goes back to where we were before so I'm going to leave the hosted service in for reference for people but I'm just going to I'm going to do this so that I don't have to this is this is just as reference right again I don't think the IH hosted service trick is good as a general pattern however there are use cases where it is the best option so it's worth it's it's worth being a a tool in your tool belt um but it's going to be one of those tools that you don't reach for this isn't the hammer right this this is this is going to be something a little more specialized not not your generical purpose okay so the other option here is don't use the Constructor right uh and it's like well we aren't using the Constructor we have zero references there the the option or I should say the last option kind of goes back to how I logger is set up in register right so what we could do is we could do public uh I or public interface I I counter service Factory and in the past I've shown people how to do clever tricks with the factory where we um register up a delegate I don't I don't have any of them in here before um I've shown people doing Factory methods with just straight delegates if you're doing it to do this acing stuff I would not use a delegate for it you can make it work not recommended okay so but what we we could do here is uh create uh how about just create a sync right okay so we could do we could do this and we could do public class uh counter service Factory I counter service Factory and then we can we can go through and Implement create a sync and now here you'll note something this is very important we have a create a sync method that sounds remarkably like something that could invoke a Constructor and then could await an init method yes hey see doing great having fun with Di and async stuff and C which just means I'm in my happy place and I have a mountain do which makes it even better uh okay so the naive implementation here looks something like this uh VAR RV gets new counter service right and then we do await rv. init async and then we do return RV right this is goodish not great but goodish uh and actually this is yeah a factory method that's called create should probably return I counter service should probably actually return the thing that it's creating that sounds like reasonable expectation right but you'll note now we've set ourselves up with something that can actually do the creation and ensure that a knit is called and finishes before the instance is handed back effectively we have given ourself sort of the rough equivalent of an async Constructor it is is right it's not it's not perfect but it's but it's close right and then we come in here and we do Services add Singleton it could because in general you're going to be adding Factory methods as uh Factory methods is Singleton if they aren't Singleton be cautious that usually is a that's a code smell that I look closer at not saying it's bad and then for example down in here in our main window rather than doing all of this stuff and taking in a counter service directly we could take in a counter service Factory right Factory and then down here we can do counter service Factory uh let's see I got to actually type private I why is that oh the those are declared public auto code for the fail uh let's see you have to be able to spell counter service Factory for this to work uh and be able to type it's a very complicated operation and so we take this specify this we question why we made all of these public and we make these things all private because that's they aren't being exposed let's not expose them unless they're needed there we go Okay Okay so we've got a we've got a counter service Factor and now we get rid of counter service for example this guy goes bye-bye and now what we can do is we can go uh counter Service uh let's make this guy actually let's do this let's just make you knowable and give you a Setter right uh let's see VAR uh counter service gets counter service ding ding boom uh let's see counter service Factory do create a sync we do await and you question your sanity I guess that works and then this guy becomes counter Service uh let's see what are you squawking about count semicolon expected okay fine I should learn how to type semic clones okay so one verify functionality and then recap so we come in here I'm just going to breakpoint so we can step through and watch this thing work okay so we come in here counter service is initially null right so we're going to end up we're going to end up invoking our Factory our Factory is going to create a new counter service going to call initialize we're going to wait for a moment we're going to get back an instance great it's now been initialized we now go ahead and call increment async we do the incrementation write the file we go away everyone's happy right and then we will uh continue and then here we click the button one more time counter serve in in this case is already set so we just set the local we don't actually go uh and reach across to the factory that's what this n check is here for we then increment a sync great do all of the work set the count and Off to the Races [Music] um let's see uh what is the point of 147 why do you need the VAR ah great question great great question so uh it would be perfectly valid to go through and just uh do this this works the the thing that I don't like about it is when you when you do it like this what you are setting up is you are you making the assumption that this property doesn't change out from under you so the 147 will check the property it'll invoke the properties getter check the value for null if it is null it'll await this asynchronous method set the new value called the setter on the property and then down here it's going to reinvoke the getter of the property and so it's making an assumption that nothing nulls out that property between 147 and 148 right if instead we do a local field local values inside of a function are thread safe by definition and so anytime you start using async and a weight my brain immediately tries to think about thread safety because it can be somewhat problematic now in this in this particular case relay command um doesn't allow concurrent executions and we don't really have another path in that's going to end up clearing ning this out on us so functionally it's not really possible um however in general I will often times if I'm going to access a property or a factory method I will cash that value in a local variable just because local variables cannot change on me between two lines of code inside of a method so you get thread safety without needing to do locking or any of that just by virtue of access the properties uh once and then never again right which is so this this counter service I can guarantee is now not null on me assuming that my create a sync always returns a non-null value of course and I don't have to worry about if somebody knows out that property it doesn't affect this execution of the method because the local value is already got that that previous instance and it's not going to change out from under it so it's one of the the clever tricks of I I will often times bring things into local values and it's not obvious to a lot of people that I'm doing that for thread safety it just works um so it's one of those things that it's it's real subtle um and it was a trick that I learned a long time ago from one of my mentors and when he told me that he I'm trying to remember how he phrased it it was kind of very blunt of local Val local variables are thread safe by definition and I went what and and it took me a minute of processing that statement I'm like local variables are thread safe by definition and I just kind of had to think through like my brain was not moving real quick that day um and it took me a little while to get through it I'm like oh oh and then there was the light bulb moment and yeah so it's also the reason why you'll see people pattern match into a local variable with a null check because that local variable can't change on you so it's great uh obviously our clear count is broken because of all of this stuff that we've done here uh it would effectively need the same same type of of treatment and it's really tempting it's really tempting to say well hang on hang on I've got this Factory and I've got this property what would be really great is if I didn't have to have this get set thing and what would be really great is if I could just you know inside of this getter just go hey why don't I just do you know counter service Factory Dot create a sync right this is this is the other place similar to Constructors where the as synness messes with people it can't you can't and the reason is because properties already have a well- defined signature your get method is a singular method that Returns the property type and takes no parameters right that's it the set method is a void method that takes a single parameter that is of type of the property that's it get set are no are nothing fancier than just two well-defined method signatures and so you can't make them a sync therefore this would NE something like this inside of a property would never work right so it's really tempting to want to do it resist temptation back away again it's just like with Constructors if you find yourself needing to have a property do something asynchronous I question why it's a property and not a method because if you need to do asynchronous stuff properties are expected to be to to not have significantly longer execution times than accessing a field right they're expected to do some validation some minor logic and then return back they are not expected to do long running asynchronous things because properties are expected to be quick access methods are expected to do more and because a property is just a facade over common method types that's the way that is one option of doing it so this gets us all the way uh into here and this is I think closer to a general purpose pattern and again I'll preface it the again with all of this asynchronous stuff there are no silver bullets everything is the answer on all of this is it depends on what you're doing but this this kind of Factory pattern is much nicer now you will you will note here there's kind of an there's kind of an issue here is this guy is now tightly coupled to this Constructor and in an ideal situation what we would really like is for him to not need to know anything about this Constructor right what we would ideally like Let's do let's put the init async back right we would really love it if this Factory could do something special for us right like not needing to call this Constructor would go a long way but because our Factory is di injected now we can have some fun right so this is the case where we could potentially leverage something like this uh create counter service right and again I'm going to I'm going to Leverage a funk here for this but uh you can go any any number of ways and calling calling the Constructor directly from inside of a factory method is okay just to be clear that is an option Factory method's purpose in life is to build things that's its purpose and if building things means calling a Constructor that's okay but you could imagine as soon as we go through and do um for example like an iog say say all of a sudden counter service wants an ilogger right now we have to now it's like a crap now this thing needs to take in an ey logger right of counter service logger and we got to we got to cash this guy off create an assign property right all so that we can just pass it here right so if your factory method is directly in evoking the Constructor there is coupling there Constructor methods are static methods um so it's wor it's worth kind of considering uh see let me Circle back to that in just a moment um but if we go with kind of this funk approach here what we can what we can end up getting away with so I'm going to do switch the order of this real quick RV gets uh create counter Service uh once again spelling spelling is important and so all we're doing with this funk is hiding the con is decoupling ourselves from the Constructor call that's it but now what we can do is we can do uh Services add Singleton and then we can register Funk of uh buunk of counter service right and then inside of here we're going to do that same provider trick uh and then we're going to do uh provider get required Service uh that's the factory but that's close uh let's see what oh because it's not of the right type okay so we go there we go there and then we are going to do we're going to make this the interface and I'm going to move these guys down here next to the other ones just so it's a little more clear what we've got set up here and so we got we got we got a little clear so counter service is still registered here this is Funk the funk Funk reaches back so provider Lambda peren Lambda is creating a a delegate of type Funk that returns an I counter service and then if we go into our Factory we're going to do I counter service Factory here and then this guy only needs to act on the interface he doesn't need to know anything else and then he can still call a knit a sync and again the big Advantage here is as this Constructor changes again maybe you add a logger or something to it the factory is now ignorant of that Constructor change and the factory can focus on what it's important create an instance make sure it's initialized then return it right do whatever needs to be done here this might be more than one line away you go right it you if you need to do like um interface segregation sometimes having a knit a sync on this service is probably not appropriate you could theoretically have a slightly different interface but then if you change that um going back and forth uh if you if you do interface segregation inside of your di container you need to make sure that you've done appropriate registrations that all have the appropriate service type for those different interfaces right in our case we're we're trying to keep it a little simple and not sprawl too much because it goes out of town um let's see so what do you think of object-oriented programming what do you think of functional written uh more imperative and a bit into math and studying more functional programming so in general like my personal bias is definitely um objectoriented uh that's a a fairly big personal bias just because of the um type of C work that I do um both to have their place I think um with all software the big question to answer is what what problem are you solving so in an ideal situation and this never happens in real life um someone comes to you and says we need a piece of software that solves X problem please create it right or we need a piece of software that does X please create it that'd be great rarely are requirements clearcut and often times uh rather than that statement being the start of work it is the start of a discussion where we figure out work um and and that's often times where I spend a decent amount of my time is just figuring out what people want um and so depending on what you're building I think there is there is often times a good case to be made for which for which kind of pattern makes sense if you are in in a lot of cases modeling particular domains object-oriented makes a lot of sense our world is filled with objects and nouns and things and often times object-oriented paradigms lend themselves well if you're trying to model something that has lots of nouns and things functional lends itself really well when the problem that you're solving is very procedural um and so you can yeah it I think my answer is I'm biased towards objectoriented but that is based upon the type of work and the language of choice um that I tend to do most of but I I think your selection really should come down to depending on what is the the problem that you're solving because that's what all software is is solving problems uh developing a database driven website okay so like I guess it depends on what the contents of the website are my opinion is that sounds like something that's probably a lot more uh object oriented because I imagine as you start building out your website most websites have a set of things that they're doing um I started building out one for managing the the backlog of requests effectively a really fancy to-do app or a to-do list app which everybody builds at some point but at the end of the day uh the problem domain was such that it was modeling modeling the objects um uh being able to build it out um and so that works well uh MySQL Mario DB database yeah you should be fine some people get very hung up on the the particular technologies that they pick um in general if you pick anything mainstream you're probably not that bad like I don't know if there's anything mainstream that I would say is horrible because kind of by definition if if it made it mainstream it means it had to be good at at least one thing nothing makes it mainstream if it sucks um uh databases about media with a focus on movies oh yeah and PHP I actually used to do a bunch of PHP work a while back um but especially with like MySQL you'll find plenty of tutorials and documentation about getting all of that set up PHP is incredibly nice because it is there there's not a lot of magic that goes on unless you pull in like one of the the Frameworks that that adds some of the the niceties to it PHP is really uh you get what you what you see it it's very straightforward and so I've seen a lot of people teach with it because it's it is there there's nothing there's nothing complicated to explain it's like you wrote this line of code it does this okay next line and you go through it so it's it's very popular for that and a lot of people like it because because there's no extra stuff that kind of sneaks in or extra things that it's doing for you um you can get some really great performance because it does nothing extra the drawback is it does nothing extra so the there's this there's always this weird trade-off between a lot of things and JavaScript Frameworks are very guilty of this of they always want to like the moment you have Frameworks that do stuff for you they implicitly bring in the author's opinions uh of of how things should be built and how things should be structured and that's fine right that's the point you can't have a generic framework everything everything has some level of opinion even the the net generic host has opinions about how things should be set up and so because of that you you end up in this situation where um if you bring in the framework you get the niceties of it but you then also have to learn the framework and the magic and how it's doing things uh uh have you ever had a case of using something mainstream to later find out something is horribly wrong like log for J so um trying to think of worst case one that that's ever bitten me so the log for J vulnerabilities that that I assume are being referenced here didn't really bite theet Community very much there was only like a handful of cases that I saw where there was like some weird interop thing because the the net port didn't have the same vulnerability as the the Java one trying to remember the Java feature that got exploited um it was basically their equivalent of reflection um which was It was kind of fun to read the analysis of it I don't know if I've if I if I've NE if I've said that it's horribly wrong there are I think one of the worst offenders was like zamon um or or zamon forms and it's not necessarily that something's horribly wrong it's that things changed or didn't work the way that that I wanted or expected like the early versions of Zar and forms were a miserable nightmare because you lacked a lot of the tools needed to customize it the way you wanted so if you wanted the the off-the-shelf experience it was great as soon as you wanted to customize it it became a miserable nightmare um and you had to dive into custom renderers and all that stuff and in a large case they've they've mostly fixed all of these problems with Maui now Maui is not quite a bet of roses there's plenty of things that are problematic or or don't quite work right and like I can't fault that team much because they're they're trying to do something incredibly difficult of I write C and have it work on all these different platforms and these different tool chains and how do I make all of those work together and like their tool chain is insanely complicated and how well it does work I think is amazing and awesome and I think it is a a very worthwhile platform to build on but there are there are rough edges in places that just feel rough and I don't know how easily they'll get uh smoothed out I don't know if I've ever been bit by a major vulnerability trying to think if there's been one the the places where we do like vulnerability scanning um like we've done a bunch of work with like FFM Peg and oftentimes once you get down to having a lot of native libraries that's where that's where things can start to get interesting like FFM Peg has a pretty pretty big dependency tree of all of the stuff that it relies on and there there's a lot there so a vulnerability in one of those just Spyro because then in order to get that thing you need that dependency patched and then this dependency patch needs to roll up and roll up and roll up and so it can kind of be a pain um but it's never really been I don't think I I've ever been directly bitten by the vulnerability itself often times because we we do vulnerability scanning um It's usually the vulnerability scanner that catches it as soon as it comes out and then we start fixing it right and so there's I don't know if I don't know if I've ever been hit by something because it was like an active exploit or anything um but yeah definitely the using things that were mainstream and then they didn't work as well as I had hoped that that I I think I I can say for certain uh I've been moving some code to arrays and for loops great depending depending on what it is that that can be a very useful method a it's unfortunate that so there's a running joke that to become a software architect you have to learn the very important phrase it depends um and I say that a little tongue and- cheek but it it is accurate um there is there's so much in software where it people would really like to be told this is the right way to do things and there's not a great there's not a great way of doing that even with some of this stuff um that I was showing tonight is there's not a there's not a one way one way to do it it and a lot of it does depend on the structure of the code that you're writing uh oh yeah what about a case like Unity 3D that has an awesome product for years and then they go and do something which makes you have to swap to using different software oh yes let me tell you a small story about that let just just hypothetically hypothetically speaking and this will be all hypothetical for a moment if you miss the the mock controversy that came up what was it two months ago similar um there was a lot of blog posts written on it so I I I won't rehash too much of it the short version is there was a change that was put into the the mock library that a lot of people very much hated because it had some very sus behavior that many blog authors liken to virus or malware right as as one of the the project owners of the the the automach library built on top of it I have a small vested interest in it not sucking and dying just a small one uh because it is my preferred mocking Library um but it is worth noting it's basically run by two guys like that's it there's not a lot of people working on it they and so and it's and it's kind of a big time sync so one if you were able supporting supporting the packages you depend on matter but he put in a change and for like a week or two people flipped out because they were they were panicking and me I I did a stream on it shortly after the the change went in and my answer to everybody was stop panicking like just don't update the Nate package pin yourself at the old version and the problem goes away um and so yeah having things where it definitely gets yanked out from under you and you panic I I think the key thing in a lot of cases is unless something happens like Unity was a little different because of the licensing change but in a lot of cases if it's a code change even if the code change happens abruptly oftentimes there's some there's some lag time before it actually is a problem for you right like people freaked out when Microsoft bought zamer and they're like oh no it's going to die and we need to all update away from it and panic mode panic mode panic mode it's like calm down and relax one nothing's forcing you to update right this second so stop stop freaking out you have time to figure out a plan don't need jerk reaction like that's the biggest thing is there's too much kneejerk reactions that happen and people flip out and go I'm going to migrate my entire project away from insert thing here that has annoyed me this week it's like or hypothetically we don't do that we wait and see what options come like with the mock thing there's now a version of the library that is completely open source and the the other stuff has been stripped out 420.69 also has the stuff stripped out so should you switch over to that other Library I don't know there's there's nothing forcing me into to not using this version right now so waiting will often times yeah yes I the the uh the versioning the versioning is also sus like just like we're going to just look at the published versions um I think the problematic version was 4.19 or I I don't remember what it was it's now been unlisted so it it no longer shows up in the query so there's that but but yeah so I I think what I would say is in the event that something like that happens step one don't panic because that doesn't do you any good uh step two don't make any rash decisions uh the first thing to do is evaluate how and when key part when do you need to to ingest that problematic dependencies change right regardless what it is a licensing change code change whatever if there's nothing pushing you to do it don't like you there's nothing there's usually nothing lost by waiting uh and then once you know you know when when your your drop dead date is for for when you would need to ingest the change because of you know whatever's happening then you go through and say okay as we get close to that let's start evaluating what options are available and figure out the migration path there uh have you used used F or qsh before uh I have done a very small amount of FSH um I I was learning it a little bit um because I was uh helping somebody out with a uh ironically a c Source generator that were building an FSH don't ask um so I was doing that um and I have I I did Java a lot in University um once I got out my first job uh because I had in University i' done vb.net and Java they told me they're like oh c is just smashing those two things together and I went oh okay and then I fell in love in C um to be able to go through and do it uh let's see uh this can be somewhat mitigated by having your own abstraction on top of third party stuff uh you swap out if different Library if needed takes more effort up front but may pay dividends later no probably not applicable to Unity though yeah there is a there is a balancing act there too because some people will put a lot of abstraction in and and and rarely does it get leveraged for anything so I will generally I guess it depends on the library there's certain libraries that if they don't play nice with the Microsoft abstractions libraries I will probably put my own abstraction on top of them um just to kind of you know put lipstick on a pig and and make it work a little better um if it if it is something that's kind of a more mainstream Library I'm probably not going to especially if it's one from like Microsoft like so it it varies a little bit on what I'm going to put a uh an abstraction over um but certain certain things that you think you if you have reason to believe you might swap it out later for something different yeah throw an abstraction on top of it if you don't have a reason to believe that you might swap it later don't right I I wouldn't necessarily build an abstraction for something that you don't think you have a reason to swap out like a good example of that is um like Newton soft. Json like I I forget 12 years something like that um Newton soft was the number one nougat package and it wasn't even close like newtonsoft ruled the world in nougat download numbers there was no question about it and a big part of that was aspnet for a while um took a direct dependency on it so everyone who spun a new ASP net project got Newton soft. Json that boosted the numbers a lot um but then few years later come along now we have system. tex. Json and so before or people would build things that were arguably tightly coupled to newtonsoft and there wasn't a big question on that because okay like you could use the component model annotations and whatnot to try to not couple yourself to it but that only Loosely decouples you because whatever you migrate to that's only an advantage if the thing that you migrate to also supports those annotations it's like maybe it's okay maybe it's not like cuz that's the problem is you can build an abstraction but sometimes those abstractions start to get real leaky real fast um and building out a perfect abstraction is oftentimes not cost efficient unless you have a reason to believe you need it so there's there's that Balancing Act there but with system tex. Json it's like oh well maybe I should put my Json serialization behind an interface um yeah help that Jason got HRI micos to work on Azure stuff yes yes absolutely um and as I understand it he also helped with system. text. Json which was which was perfect the but so there's the question of well sh shoot should I put an abstraction there and and I don't know if there's a clear-cut answer um I'm dealing with a project now that there's a lot of stuff that's tightly coupled to to newtonsoft and we're like well shoot should we switch over to system.js like there's some advantages there it's faster but it has less features is that is that worth it at the end of the day it's a bit of a judgment call does it matter like do you care about that perf maybe maybe not so it it it varies a little bit um yeah great questions like it is a it is an interesting thing to to go through of when when do you put in the abstraction like I said I I think the answer is it depends which is which is just great in terms of like a WPF um application I generally will put in some level of abstraction because you you've got your view your view model and then there's usually some sort of like business layer that that comes in there and you can kind of start to tease things out a little bit um because if you put all of your business logic straight in the view model like that works for a small application but it doesn't scale well so there is a I should say this I got bit at one point many years ago because I had put too much logic too close to the view models um and then then there was a desire to say oh by the way we're no longer going to run it in a WPF application we want this same logic but we want to go run it in the this cool new thing called net core and run it crossplatform and I went ah crap because it meant having to go through and do um a lot of refactoring to kind of break those things apart so having having that idea up front of what things should rev together and what things should stay together is I think a skill that is best taught by the School of Hard Knocks um okay one other thing if you haven't seen this this this is something everyone should read um you'll note this is an amazing paper written June 26 1999 um some of you may not have even been alive when this was written and this is very applicable today there is a lot of things here some of the big ones um the idea of like the peac mill growth keep it working in shearing lers shearing layers these things here are excellent absolutely excellent of being able to kind of think through this um but definitely recommend this uh to anyone who is at all responsible for making design decisions in your code base right some people are literal code monkeys who I give I was given bug to fix I fix bug I move on right great not a lot of thinking involved in that um but as you start kind of moving up into kind of more senior and architect level things those types of decisions start to come on to you more and more keeping these principles in mind will take you far so definitely definitely worth reading okay cool okay well I think I'm going to wrap it up for tonight since I've got a bunch of stuff to do um oh hang on another interesting question what do you think of the new coding style in the newest C where classes are what I would call headless uh confused the hell out of me yeah so it's the syntax is not that different in my head from the record types but the record types are also fairly new so if you haven't seen those it's kind of similar there cuz I I think you're talking about the new primary Constructor feature here we'll pull that up hang on and there we go uh C 12 I think this is this is the features uh this guy here yeah where you get where you get something uh let's see initialize property create a mutable struct why are they all doing struct uh yeah so you get something here that's very akin to record types uh in my mind um the really interesting part about this if you play around with the feature at all these things are the exact inverse of a readon field so a readon field is only assignable inside of a Constructor these things are only assignable outside of a Constructor which I nearly died laughing when I saw that like I I get it I I get what they were going for and why they did it it just I don't know it didn't it didn't land it didn't land well for me now with that said it's probably a feature I'm going to use um because this kind of compressed syntax is kind of nice in some cases and actually let me look real quick I think we actually added it uh uh primary Constructors ha yes boom there we go uh yeah so in your in your sample there of public class uh string bar which is you know public got a typo there or a bug just gonna just going to quickly screenshot this real quick because that needs to go to a particular team member to fix um there so there is a these things here do end up generating um uh those fields behind the scenes um and just with the way the compiler sets things up they are only assignable outside of the Constructor so you functionally get kind of a read write level uh field you you can do initialization with it with the the properties and whatnot um to be able to go through and set it up so it's it when I say it's the inverse of readon I I don't mean the readon keyword I mean functionally it's it it's um backwards in my opinion which again was just one of those things that from playing with it I I just thought was hysterical and when oh that's funny so uh there was that um yeah but worth uh again I think it's a feature I'm probably going to end up leveraging like I wish I wish I had some level of control of these and I could like have them declared with the read only modifier and all that stuff like I get it like that I I want a level of customization that's not there and with C what we've seen happen repeatedly over the last several versions is they introduce a feature it's somewhat limit lied in its initial release because they can only get so much done in a year and then they start adding on to it and it becomes a more full-fledged more useful feature in uh in future releases and I suspect that's what we're going to see here is that this is this is what they could get done and get done well in an initial release and we will potentially see improvements to have it not feel just kind of smooth off those rough edges of things uh yeah and a bit confusing for those that put underscores on their fields yeah I'm definitely I'm definitely guilty of it and and yeah you're right it it it starts to look weird and um we have a version of so in tetch the company I work for we have a set of coding guidelines and features like this wreak havoc on it because there's there's not a good like there's no good answer of what to do like even with records we had the same problem of how do you name the positional parameters in a in a record because the positional parameter is used for Constructor parameters as well as properties well shoot those had different naming conventions previously but you've got one thing that's being used in two places crap like that doesn't work anymore and it's the same thing here you've got one thing that's being used for multiple things and so you have to uh basically pick which which thing you're willing to sacrifice more um and what option is just don't use the feature but it's like well it's kind of a convenient feature so it's probably getting used for what it's worth so okay there we go now I'll wrap it up because that's that's where we were going thank you everyone for coming and hanging out hopefully that was useful uh like I said I I plan as soon as I get the updated nougat key to roll out the uh material design 5.0 release soonish or at least get beta betas out so that people can play with my new tree view if you do play with the tree view please drop me a comment or a message or let let me know how it goes even if it's hey I tried it and it worked well that's useful data points for me so um I I am going to start pulling it into a couple projects and see where it lands because I think it's it's pretty good it's pretty good I I don't doubt that there's things that have been missed or probably could be dealt with but I think it's I I think it's probably going to work for like 80% of people so if you want a treeview and you want it to uh support things like multi selection I got you covered got you covered so as always everybody happy coding see yall next time
Info
Channel: Kevin Bost
Views: 1,594
Rating: undefined out of 5
Keywords: programming, C#, WPF
Id: rCdUanCQ6ws
Channel Id: undefined
Length: 132min 1sec (7921 seconds)
Published: Sun Oct 29 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.