Blazor Data Virtualization: Carl Franklin's Blazor Train Ep 39

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi i'm carl franklin in this episode of blazer train we're taking a day trip through the land of virtual data the goal of virtualization is to display large sets of data as efficiently as possible retrieving small chunks on demand giving the end user the illusion that all the data is at their fingertips yet only getting what is needed to be displayed as it's needed to be displayed in.net 5 microsoft introduced a blazer component called virtualize daniel roth actually showed it to us in episode 23. virtualize is a container displaying items in a list but rather than requiring all the data to be loaded before the page renders visualize asks you for the data that it needs to satisfy the requirements of the ui as the user scrolls through the data so i'm first going to make the case as to why we need virtualization in some scenarios and then i'll show you a few tricks with virtualeyes by the end of this episode you'll be taking off like a bullet train down the tracks to efficiency town man the metaphor is getting old in that well anyway that's all coming up right now right here on blaze a train all right so here we go guys i've got a brand new blazer webassembly application that's hosted and you can see i've got my three projects here and the first thing we're going to do is we're going to add a model because everything starts with a model i'm going to add that to the shared project it's called person now person uh has an id a first name and last name and a bio just some stuff to show and also photo url which i've hard coded to this generic person png so let's add that guy to the project right over here that's going to go in the client web root and of course because it's in the web root i have to make sure that i tell visual studio to include that in the build copy if newer all right so we need some data and i'm not gonna use a database because that just complicates things so what i'm gonna do is add a little console application that generates some data using the famous lorem ipsum set of words and uh just let me do this here i'm going to add a new project and it's going to be a net core console app and we'll just call it create items now create items some reason is uh defaulting to net core three one i want this to be net five so i'm going to change the target framework right there not sure why that happens i mean this is a net five project don't know all right so create items needs to have access to that model so we'll add a project reference to virtualization demo shared and now the code so i borrowed this lorem ipsum generator and modified it and i didn't look too far into who did it but there's the url right there if you want to go out in and check it out now it is in code plex which has been archived but um you know there it is so essentially we've got you know the the the generator here we've got the lorem ipsum dolor blah blah blah strings and um we're we we're telling it how many words we want the paragraph length and uh all of that so then i also have modified the random string array tool that was included with this project to just randomly pick stuff all right so needless to say this is the real code right here um i'm going to create 5 000 generic person records we want a large data set because that's what this is all about dealing with large sets of data so i've got a for loop 5000 times create a new person the id is just going to increment starting at one and the first name and last name i'm just going to pick a random single word from from the from the latin and bio i want 50 words so i also have a couple things to clean up and capitalize the first letter for the first name and the last name and i'm also capitalizing the first letter the bio just little stuff all right so now i have my people i serialize that into json and i save that in the current directory to people.json so we'll set that as the startup project and run it all right now let's go find that data file and copy it to the server project there it is right there people.json copy to the server all right and we're going to go back to the server making that again the startup project and we don't need any of this anymore and now we're going to start on the server side we're going to leave the client side alone for a minute we're going to start with a manager in a folder called data and this is going to be called persons manager all right leaving aside what's up here for a second let's just take a look at this so in the constructor i'm loading up that people.json file and i have a publicly accessible list of person called called people so now there's this stuff with locking and total records in my demo i'm going to show from the server side with a console right how many records have been retrieved since we clicked on the page so that's what i'm doing here because singletons aren't thread safe i'm going to make this a singleton so there's only one copy of data um we're just going to wrap this in some locking logic you know when we return the total records also when we increment the total records we're adding so many to it we're locking it there and when we reset we're going to do that anytime we change pages you'll see we're going to set it to zero but it all has to be locked okay now let's get to the controller here's the api controller all right our person's controller injects a person's manager and that's that's gonna be a singleton we'll get to that in a second um yeah we're gonna start with a get all right i'm going to return in the first demo 5 000 records it's about 10 megabytes that's how big this file is right here and we're going to return it all and see how long it takes to make the case for virtualization now i have an end point to get a single record by id and you'll notice that here i've got this total count which i'm incrementing right and then i'm showing on the the right line i've got a little duda there and then i'm showing in the console window that we retrieve this record and the total count actually i'm not even going to end up calling this but you'll see that that we're going to use this same total count reporting thing so here we're getting the count here we're resetting the count get range is something that we can say we want from the start id to the end id and these are the ids the numeric ids themselves the keys of um of person and that's pretty easy to understand we increment the count with how many people came back and then we say retrieved record start id to and then result last id because you know you might want to it might want to retrieve records 4 999 plus 20 and of course there isn't anything past 5000 so it's only going to return um you know up to 5 000. so that would be 2 in this case and get by ids is another one where you can actually pass a list of integers a list of ids they may or may not be consecutive but it will return all of the person objects that match the ids in that list so we're going to be using all of these things now i told you we need to make this a singleton so let's go do that and there's another thing we need to do too and uh for this i've got a tip all right and here's the tip first of all you can see i've added persons manager as a singleton but i've also changed add controllers with views to add these json options to set the property naming policy to null and it's not very clear what that does but if you don't do this and you just use the default json stuff in the controllers you're going to get back json property names with camel casing so first name won't be capital first name it'll be camelcased first name and the end result is you know i don't know what to do with that you on the on the client we don't it doesn't map to anything i don't know why this is happening but it is just something that i noticed and that is the fix and that's your tip all right now we're done on the server let's go to the client and we're going to add a services folder and to that we're going to add an api service class all right here's the api service we're injecting http which we need to grab it's been pre-configured in program cs so that it all automatically uses the base url for our for our project and here's my get all it's pretty simple here's my get person by id my reset record count my get persons count get range start id end id and get by ids which is actually a post if you remember get by ids we're passing that list all right so the next thing we need to do is add the api service to program cs as a scoped service and are using statements to imports razer and now we can start getting some data let's go to the index page and hijack that as i always do all right so looking at the code i've got my uh list of person people i've got a boolean loading and i've got a load people now why am i not getting the data in the uninitialized async override well the reason is i want to make a clear distinction as to when load people gets called when the data gets actually called so that i've got a button unclick load people so we can see the time it takes from when i click the button to when the records show up so i got loading equals true and i'm saying state has changed because that changes what shows on the page and i'm calling my get all from the api service and then if we do have people i'm going through each of those and i'm showing a div with the first name the last name the bio the photo url and if i'm loading then i just say loading otherwise i'm not loading and i don't have people so that's the default i'm going to show that button all right so let's see how long it takes to download 10 megabytes of objects 5 000 objects and put them essentially on the page in a list here we go load people wow okay that took what six seven eight seconds something like that almost 10 seconds and now you can see that i've got all the data on the page 5000 records let's just for fun let's pull up the task manager and see how much memory edge is taking up right now there it is right there 442 megabytes wowsers and maybe for comparison we can run this again and see what it is before we press that button all right 149 so geez 300 megabytes added to the browser's dom just by loading all that data so it's slow and it's a lot of memory and that is why you need virtualization if you're going to do this now brings me to my next question why are you actually loading 5000 records in a browser does your user get any benefit to seeing 5000 records all at the same time and the answer most of the time is no but sometimes you get those customers and they just want to know that the data is there and they want to be able to scroll through it and so that's the case that's what we're doing we're dealing with large data sets so we're going to add a virtualized page which uses the new.net 5 virtualize component to sort of make this faster and more efficient here's our virtualized page before we go any further i'm going to update my nav menu with all of the pages links to all the pages that we're going to have here's the brute force page the index our virtualized page and we're going to do caching after this so let's go to the virtualized page alright so the virtualized component accepts a context and you can see i'm setting that to person and that is the model that we're using but it's also the context so i can use at person to access the data on that person and i'm also telling my div that the key is person.id uh and that's important if you're using virtualize to give whatever your container is for your object a key uh so that's about it now we have this placeholder here which has the same styling as my div above but it just says loading and you might see that flash uh on the screen when it's loading something it's going to get the next records but the key here is items provider and items provider is a method load person's virtual is what i have it set to and this returns a value task of items provide a result of person and passes the request i've got this last person id and records to read records to read is the number of records i want to retrieve each time and i'm setting it to 20. and last person id is a is that is going to be 5000 it's how many records are in the total set and we need to tell blazer what that is so let me get the rest of this method on the screen all right so if the last person id equals zero that means we haven't yet retrieved it so i'm resetting the record count on the server remember i told you i was keeping track of how many records are read and so last person id is zero because we've initialized this page and we've navigated to it so then i'm calling get persons count to return last person id so that should be five thousand now i have the start id which is the request start index which is zero based so i'm going to add one to it and then last id i add records to read and then i call get range on my api the start id and the last id and now i'm returning a new items provider result of person passing people which is my which is what i got from the api and the last person id so let's see that working so we go to virtualize the first thing you notice is boom it comes right up now let's monitor what the server is spitting out for records red so there you go you can see that 21 records have been loaded and just so that you can see this i'm kind of in front of it i'm going to take myself out of the picture now watch this as watch this number total 21 as i move down so i'm scrolling down and now all of a sudden boom some more records have been loaded right but you didn't really see you didn't really notice it right it's just scrolling down now one thing i want to point out is if i press end now we've got the bottom of the list right 4987 to 5000 but we've only loaded 224 and now if i press home to go back to the top it goes and gets one to 21 again okay it's fast but it's reloading stuff that it is already loaded alright so there is the simplest way to virtualize data it's not cached but it is virtual so you're going to end up getting maybe getting records again where you're scrolling around but at least you're not grabbing them all at the same time now let's move on to caching i'm adding a page called cached okay so everything here at the top is the same i'm virtualizing i'm not doing anything different there however my load person's is now called load persons cached and i've got some neat things going on here that i want to show you so the same way that we were loading last person id if it's zero we're doing that and now i've got my people which is my list of person where i was just returning that now i'm creating a new list and i've got to add stuff to it so i've also got this dictionary you can see that right at the top right dictionary int person people cache equals new dictionary in person it could be a list but dictionary just makes it easier so this is going to be my cache and essentially what i want to do is go through each of the items start id to last id and look to see if it's in the cache and if it isn't cached i'm going to add it to my ids list because i've got a new list of ins otherwise if it is in the cache i'm going to pull it out which try get value does and i'm going to add it to the return list so uh what about the ones that aren't in the cache now i have to go retrieve those and that's why i made get by ids and get by id is essentially we passed that list of the ones that were requested but not in the cache and then we're adding them to the return list people and to the cache at the same time all right so it's a little bit of logic but the end result is pretty interesting let's take a look boom comes right up of course uh and you can also see if i get myself out of the way here and if i zoom in here you can see that we've retrieved 20 items total and as i scroll down just like before i'm going and i'm getting just the records that aren't in the cache but you can see here now it's like oh i got 20 records then i got 15 records then four records right so it's a little bit more efficient it's only getting the things that aren't in the cache and if i go down to the bottom by hitting end now i've only got 87 in the cache but if i hit home look at that i haven't gone back and got any more records zero records right so i've only i and as i you know go through this of course it's going to pick up more but i only need to go through it once to get them all now i'm going to lean on the page down and watch what happens that's pretty freaking fast isn't it right so there you have it um the next thing i'm going to do is bring in a friend of mine from developer express don webbie a to show us what they do with virtualization in their blazer controls all right well as promised don webbies here from devexpress hi don hi carl and we've just seen a couple of ways that we can use the virtualized component in blazer five but it's interesting that when you have a third-party tool like a data grid you want to do some editing something more than just list items that control can't necessarily work with this virtualized component and probably isn't going to but you guys have put your own virtualization into your data grid for example so that's what we're going to show here yeah you're totally right the grid is not a good candidate to pack inside this virtualized component well like you said we already have our own virtualization mechanisms in place and yeah if you take a look at our data grid you'll see that we have several ways of doing so and yeah let me guide you through how to set this up with the data grid to fetch the data whenever it needs to and it can do a bit of more tricks as well if you have set it up properly so all right well the first thing let's do is add the devexpress blazer tools to the project here and if you're a devexpress customer you've been through this before uh you get a private nuget feed and you can just go search for it i'm adding this to the client project by the way but it also adds to the server project i'm going to install devexpress blazer here just take a second all right so the next thing let's do is add uh devexpress blazer to program cs on the client and there's a another step that we need to do here which is in the web root index html we need to add the style sheet very important here yeah otherwise things look different exactly all right and also there's a few namespaces we need to add using statements for imports so we'll do that and now we're all set up to use devexpress well um i want you to sort of tell me what's going on with the stuff that i'm going to add next and starting with the controller we need to add some stuff to the person's controller to support the the grid so the first thing let's do is this uh model binder and why don't you explain what this is while i'm making these qualifying these namespaces yeah that data source load options is something that we send across from the client to the server which holds all the information like filtering sorting grouping etc and it needs to be like serialized over the wire and we use this model binder to decode it again on the server so we can query our data store all right so then the method or the endpoint is going to be in http get and this load result is what's coming back but it's also accepting a load options yeah that is the uh parameter that i was just uh referring to right this this this action method receives this load option and it returns a load result and this is a mechanism that we have already introduced in our devex stream product as well we are bringing the same mechanism over to blazer so web api knows that once that data source load options object is expected it will use the appropriate model binder to decompose it into a real world up so this is sort of taking the place of you know what we were doing here get by ids or or get a range where we have a start id and an end id there's more that you can do with your grid other than just get a range you can filter and you can sort and you can do all that stuff so all of that is encapsulated in this load options and then you're using your your own devexpress data source loader passing the load options and the entire people collection and that will give you a result yeah exactly yeah and the cool thing is that this also works if you are using an entity framework collection so if you want to query out of a database you can use entity framework or you can use our own xpo orm tool and the data source loader will yeah create an iq variable to properly get that data out of the data store all right that's pretty cool so now let's add a method to uh api service to support this and i'll have to qualify these things as well load results data source load options and of course we'll just clean this up here and also this is an interesting thing because i noticed that in the controller like we didn't we didn't specify a parameter here for load options and yet it's coming through what what kind of diabolical magic are you uh wielding here uh what we are doing is uh that convert to get request yuri that method that will basically append query string parameters to the get people dx and that also works with that if you use a web api endpoint and you don't specify the pattern in the url it will also check those query string parameters and it's being added as a query string parameter right so it's encoded correctly to to go in the query string and you're you're confident that the query string won't ever be too long based on the options no that will that will work yeah okay you've thought about that carefully and uh yeah all right work well let's see what happens here i'll open the old nav menu and add a new nav link there we go and now let's add the page and then we can talk about what you're doing there so this is a dx grid demo page all right so let me uh remove this and you can explain me what's going on here yeah so what's interesting to mention is that the first declaration with the dx data grid is that we specify a tip a t which is the model type that is bound to the data grid so we know what kind of models we're dealing with the second parameter the custom data is not a collection with items but it is actually a call to a method and the method is called get some people yeah and you have coded it correctly here so what it does is it or it basically receives two parameters which is a load options object and so the same one we've been talking about on the server and it can receive a cancellation token as well in case you want to abort the mission while it's transferring data it will be canceled as well and what we then do well we uh modify the load options to take 50 records with the first the first time yeah yeah and and then uh yeah afterwards we're executing the get people dx from the api service where we pass in the load options as well as the cancellation token and what you'll see is that it will fetch the data only the data it needs and so does it cache the data also because that that's a really important thing is we've just seen that if you're not caching you can repeat you know go get the same records that you have before yeah we have some mechanisms in place as well to uh to do the combination that you did as well with the cached uh virtualization a lot less code yeah it saves a bit of code and remember that the uh there can be one line left which is the reset record count but that's for testing right that's just for testing yeah all right well let's crank her up and see what happens there we go all right so what i'm going to do now is show the output from the server and i'm going to remove myself from the picture all right so now you can see as i scroll down that i've got 218 records coming back from the server and i keep scrolling right let's do the thing where we scroll all the way to the bottom boom there's the 5000 but i've only got 366. so what are some of the other things that we can do with this now well what's also quite cool is that if you now we have enabled the filter box on top so if you would now at the first name if you would uh search for a uh just put in a couple of letters and tab off of it yeah there we go and what you'll see is you might you might want to put a breakpoint in your web api so you can actually show the load options what it what it contains okay let's do that again what you'll see is that the query will be sent over all right so let's change this and i'll just continue we'll get another do another filter here um b a i don't know all right so now we look at load options and what should i see here there should be a filter oh yeah there it is first name contains ba well we can also use uh column sorting so if you want to have if you want to click on the header you'll enable the sorting of that column and also that will be done on the server as well because it's all in that load options object being sent over to the api allow yeah ala sort equals true and all right i think if you don't specify anything with the columns you can sort all of them all right so sorting 5000 records but virtualized let me uh step out of the picture here again and sort by first name all right so it's sorted on the server but now i'm only getting i'm not returning all the records and sorting them here um i'm just getting a sorted version virtualized and you can see the numbers are all over the place because it's sorted by first name that is really cool yeah and if you click the first name again it will sort descending as well and it'll do the same trick wow sure yeah nice very nice yeah this is this is one of the cool mechanisms that we have and this is the combination with the load options object which is sent across and the data source loader method or the class the helper class that we have on the server that will query the correct set depending on those load options so that's a very powerful feature to uh to do the virtualization as well good stuff don thanks thanks for uh helping me with this and showing everybody what you can do when you're when you're devexpress and you have access to all the little bits yeah you're welcome carl all right back to you in the studio carl well i hope you learned something today i know i did i learned that the whole train shtick will only get you so far but hey i still get to do my job without wearing pants hey thanks for riding the rails with me today this is where i jump off i'll see you next time [Music]
Info
Channel: DevExpress
Views: 2,816
Rating: undefined out of 5
Keywords: Developer Express, DevExpress, Visual Studio, Microsoft Development, Software Developer, blazor, blazor train, Carl Franklin, Microsoft Blazor
Id: pRUkGmOhM4A
Channel Id: undefined
Length: 35min 20sec (2120 seconds)
Published: Fri Feb 26 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.