C# Expression Trees Explained (Reading/Creating Examples)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome everyone today we're gonna be talking about expression trees this is a follow up after the video of reflection as the topic sort of stands in the same area of your code acting as data that you're going to operate on so if you haven't watched my video on the reflection I highly recommend you go watch that I'll leave a link in the description and maybe an annotation somewhere there if you understand the code that you write is also data that you can use to do stuff then you're fine and also just because your code is data that means data and B code okay so when you receive data you can create code and this is what expression trees are all about so Before we jump in I just quickly want to explain the tree data structure so there is a couple of different trees that they are primarily we're gonna take a look at a binary tree which is more common one so for example if we have a map expression of something like one plus two right what's gonna happen is before we can add anything we need to know what we're adding right so we add one and two so we need to know about one and two before we can add so what we do is we say right we want to add but what do we actually want to add or is we want to add one and we then we want to add two okay so the circles are called nodes and nodes can also be represented as classes objects etc right so it's some kind of an object that holds some kind of a value of some kind of type so for example here our we have types of integers and the values you can see them and at the top here we actually have an operand right so the type is an operation and the exact value is a plus right and the way you traverse the tree is you say right what do we want to do we want to do plus do you have anything on the left yes we have one do you have anything on the right we have two and then we add them together okay so you always Traverse from left to right and this is essentially it to the tree data structure it can be it basically if it grows in size so for example if we multiply by three three with the multiplication is done first so we kind of have to do it in reverse so we still do the plus first or rather we write it first because it has to come last or after the multiply Hey so we do plus and then we say right if we want to plus what do we need to know about brackets here to make it easier we will need the one okay so we have the place on the left hand side we have one on the right hand side we have the two x three so before we can multiply anything we need to know about two and three so the next node will be multiplied and then on the left hand side of the multiply we have two and on the right-hand side up three okay so this is pretty much it this is what expressions are we can also take a closer look at them and the linked bad example so for example if I say VAR a equals 1 plus 2 so we're gonna do exactly the same thing I've just did so there's no but but primarily I want to go to this tree window and what I'm gonna do is I'm gonna highlight 1 plus 2 okay and here I assume in real quick so what you can see is we have the add expression as the top node going to your left we have a numeric literal literal expression and then on the right we have numeric literal expression again and and at the bottom green ones you can see the value that the object holds and if you can see a common pattern what we have is a expression appended to the name of numeric literal or an add so this is essentially the roots type of the object called expressions an abstract class from which all other classes inherit from ok so again we'll just build on this example a little but we'll multiply it by 3 the same as we say maz I've drawn it so the the whole main function is a little big so primarily what we're going to be dissecting dissecting is 1 plus 2 multiplied by 3 here you can see we have the add expression on the left we have the one on the right we have the multiply expression multiplied expression holds the value of x here numeric literal on the left hand side and numeric literal expression 3 on the right hand side ok so this is pretty much what an expression looks like when you essentially you write some data and then it generates code based on that data okay just so happens that this data is c-sharp syntax right so let's go ahead and bring a little example to sort of help us jump into the world of expression trees in c-sharp so what we can have is essentially a function which will say returns an integer and we'll say it that it returned it returns a 5 so we'll just trigger the function and we'll get five so we execute five and then we'll dump this dump we'll just output it to the console on the right here we'll get number five here so all this function does is outputs a five what I want to do now is let me just copy this line and I will wrap the funk in an expression yeah and I will offend underscore expression on the end let me put some space so if you've ever seen this you would have probably seen that seen this and if you used entity framework or or autumn a per but essentially what this does is it basically says well we are expecting a function that returns an integer go ahead and store the information about it okay so five expression is not a function it is rather information about a function okay and the primary difference between these two is that for five function what we can have is we can have a method body inside of the function a with the expression we can not actually have a method body in which we can return okay so this will give you squiggly lines because the method body consists of statements which do not not exactly evaluate into expression trees reliably so let's go back and let's just output the five expression and let's see what we get right so just for sake of proof we're gonna trigger the function and it's gonna give a squiggly lines you know we cannot execute the method right so what we can do is let's just go ahead and dump the expression and what we're gonna get on the right-hand side is essentially all the information about the expression so here we can see the node type okay so as I was drawing these out the circles or rather there are no arrows so these circles each circle is a node so in our case the node the root node is lambda since this is the first object and then at the body is a node that is called constant so node type constant and so this would be the constant this would be the lambda and on the right hand side we would have rameters okay so these parameters there are no current is zero currently so what we can have with parameters we can have multiple parameters and then with the body so this body right here I kind of didn't write body here but essentially this body contains this node right so it's just a singular expression so this should give you an idea that the function that you assigned to an expression what this really is is just information about that function or rather that expression or that expression tree so you can traverse that is this expression tree and that you can do stuff like derive from the information of the function that was written so this is some data that you're passing into the expression which you can traverse that so this is how entity framework will read the expressions that you write and then based it will check what you have written and based on that it will generate an SQL query a but then what you can also do is use the expression API to create functions so once you have an expression and you actually want to call the function what you can do is you can grab the expression because at the moment it's just data about a function you can go ahead and compile it okay and after we have compiled it compiled it we can run it and see that it's indeed a function so once we compile it again it's that function that we defined there and what we can do is we can actually call it and we will get a five in the end okay you're not comfortable with the two brackets on each side what we can do is we can call invoke instead and that's still going to be a five okay so I don't want to dwell too much on talking about expressions it kind of aimlessly is so I've prepared two examples what an example is the reading expressions where essentially we're gonna provide some expressions and then we're gonna try to derive some day and then we're gonna read some data and create expressions or create functions at runtime hey so let's go ahead to the read expressions and what we have is a user object and then we have a URL so what we're essentially saying is you're gonna call an API that is gonna give you back a user object and what I want you to do is select some fields on that object so for example when we create the URL we want to select the name and age and would like it's the framework how you do the Select statement here what we're doing is we are including feel the fields in the URL right so we're gonna if we add the name what it's going to do is it's going to add a query parameter of name to the URL right and then the response of that is going to be a user object with age no and name present okay if we include both both are gonna be present this is sort of the set up so as you can see here what we can do is we can type in strings and this is a real easy solution but if you use strings what can happen is you can flat-out add a property that is not meant to be there and typos can happen all the time okay and there is no intellisense over your strings unless maybe maybe there is grammar checking if you have some plugins in your ID but generally this is a very mistake-prone approach and unless you're very familiar with the api you're going to be making these mistakes so what you can do in this particular situation is you can use the object right so you have an object and you already know what properties and be returned so the data the information about the properties that can be returned are already on this user object all we need to do is find a way where we can say right grab this user object and then select a property okay and this is precisely what we're gonna do instead of specifying strings which we want to select instead we're going to specify properties that we want to select right in this case we have name age so what I'm gonna do first is not soil this this example too much we're just gonna go ahead into the example here I'm a PE user object here and what I will do is I'll just show you my approach how to essentially extract data because all we want to know is what property do we want to request how do we select a property usually right so usually what you would do is you would have a user Oh a new user and then you would say user name right so you this is how you select the name you can then dump it and if you've set the name to be something you're gonna get that output okay so the primary thing that we want to simulate is this so grabbing a user object and selecting a name and we can and how do how do we put this behavior into an expression so this is what you need to think about you need to put this behavior into this expression okay so you don't don't think of this as the function you're gonna be executing think of this as c-sharp syntax that you're going to read as data so instead of writing this string you're gonna write c-sharp syntax which is gonna get checked at compile time okay and it's not actually gonna run that about just using that as data so what we can do is we can create an expression and for this expression what we'll need is we will need a user and we'll need a name again I'm gonna repeat this we will not actually need a value of the user to pass into this function we're not writing a function we're writing is some c-sharp code that is purely there to be read so we're gonna create a function which is going to accept a parameter of user object and it's gonna return an object right so because this is gonna be some return type we don't care if it's a string or an integer we just want to return something so once we have this expression well let's call it expression and I will say that we will grab a user and then we will select username hey so this compiles if we want to select H and if we make a typo we get compile time checking we cannot select properties that we haven't haven't registered on our user object right so we can't make a mistake besides making a typo on the user object itself okay so let's go ahead and dump the expression and let's go ahead and take a look at what we can find here I'll make this a little bit smaller so no type lambda primarily I'm looking for the name or the age and all I want to do is end up outputting as the age and name string okay so once we what are we selecting we're selecting age so the age will have an honourary expression note type convert so expression is the base type and then there's a bunch of classes like an area expression or property expression which is really a member expression just a little bit confusing of what what actual types you can get sometimes you can get the name from the note type sometimes you can get the name from an area an area expression and this is just a trial and error process at this point so yeah so what we're really after is the name of the parameter right so we want to get this member and we want to get its name because this is how we're gonna derive what string we're going up into the URL a so let's go ahead and zoom in on the body and what I want to show you is that at this point the note type is convert so it's an uneri expression and if we select the name it's gonna be a different note type so note type is member access okay the reason why it's doing is because it needs a conversion from integer to object okay but for a string it's a little bit easier string is kind of an object but essentially you can see the difference that the body can be any expression so what will happen is this body is actually an expression right which is returned from the lambda expression so lambda expression is a concrete class and expression is an abstract bus from which the lambda expression derives and we'll get to using the actual classes and creating them in the creation example but yeah so all I'm alluding to is that if we go to the body and then we try to grab the member we won't be able to because it's not there right member exists on the property expression or member access expression and if we try to do the same with age so for example now we have the node type convert or uneri expression if we try to select the operand where our what's called our member lives oh if we try to go to operand right we can't because what is an expression we need to do a type conversion so because this example is a little bit harder let's go ahead and run through this first or actually let's let's do for the previous one so let's go ahead and grab the body put it in a parameter just like that and what we will do is we will say let's start with the name first run this and let me just dump the body again we can take a look at it while we're working so you can see no type member access so what we can do is do and if and generally how I sort of get a feel for what's last to convert the body to if body is property expression this may not work so I don't get anything what I will then try to change this to is something like member expression a or member access expression so then I can use this and basically then on the member expression I can zoom in on the member and grab its name and then dump it and maybe even to lower it - what's it called to fit it with the URL put a semicolon on the end and at the end I get a name hey otherwise if I select the H I'm not gonna end up with an age at the bottom here because the e body is a rather and uneri expression so this is again if you try to then orientate by and based on the node type so if you say body is invert expression like I don't know what convert expression is it looks like I'm not sure why this API is so confusing but this this doesn't help but the things that you can do with this end up being quite impressive so on our expression Yui an area expression and then we can go ahead and zoom in on the operand and again operand if we hover over it it's going to be an expression so again at this point we will need to do another conversion where if I'm not gonna do too many examples but essentially here I'm just gonna convert it here because I know what it's going to be and it's going to be member access which is what was here so we're gonna ask the to member access and then be able to grab the member and then the name and then to lower so we're gonna do the same thing that we did above there and we're gonna dump it okay and by the time we run this at the bottom we're gonna have age right so the point here again I'm gonna reiterate this is that we are not executing the funk that we right here this is code as data so we have written code just to use it as a data structure okay and it comes in the form of expression trees which then we can use something like this to check it okay so let's go ahead and hook it hook this up because on here we can sort of do a stripper am string array to select the fields let's go ahead and replace all of these question marks with yield selectors and what we're going to supply is an array of expressions on users to objects it's like that okay so now all we have to do is grab the rest of this function let's say put it here the expression we will have to put this in a loop so we will have to look through all of these and grab each individual one so of our less string results so I'm gonna accumulate all the results here so instead I need two or so yields equals new list dope let me call in there so this is where I'm gonna get my results then I'm gonna put a for each loop where I'm gonna iterate every single selector so selector in not getting in tell us just a little bit what's it called uncomfortable say the least you really I like I like getting my hand hold but you know at least I get the red there hi so at this point we can grab the selector we grab the body instead of dumping anything what we're going to do is for these spaces a little bit yeah instead of dumping but we are just going to add them to fields so fields and to lower and then the same thing here I'll copy this again this is not the most correct implementation this is just a simple example to kind of let you know what can be done so we grab all the fields at this point we're gonna have a list of fields and then we can do the same sort of thing here and supply the fields again so now instead of giving this strings what we do is we say that we have a user and on the user we will select the name and then again we can select a user and we can select age okay so at this point we're just selecting properties and this is checked at compile time so we cannot get typos unless we make it on the object itself which is a little bit easier to spot during code reviews and stuff like that so let's see if the results are identical so we will say number one here and number two here let's go ahead and run this it looks like I have not supplied selected fields to this part of the code so let's go ahead run this again and here you can see the URLs that we generate to the API are identical except one is not error-prone however there is a bit of complexity that hides behind it but if you have a large code base with a lot of API and point calls this might actually save you a lot of trouble okay so this is aced again the to kind of summarize this part is we are writing code not to execute it but rather we read it as information a and this is for example what awesome operand entity framework do or rather use there are like reading expressions rather than creating creating them so let's jump to the next example so here we're gonna have we're gonna be creating expressions this can be super useful and hopefully this example will be able to show you why so let's say you're building an API and you want to let the user filter your yeah maybe filter your results or let's rather say order okay so you want to order yours or results or you want to filter your results usually how do you filter right so what you do is you do a dot we're close in your link you put a little you know expression looking thing right but we're gonna filter everything where age is higher than five right so usually everything is done with Lincoln c-sharp these days what if you could use I don't know a string to construct some kind of depression which you can then pass in to a where clause or a selector right the possibilities are endless here so here's what we're gonna imagine right we are going to be passing a value in the query or header or body which is then going to be used to either select a field or filter it right in this case it's gonna be selecting so we have is some class where we have a ward and a number and here I am instantiated that instantiating that some class and if I want to select a field usually what I have to do is I have to put a if statement right am i grabbing a word if I am return the word am i grabbing a number if I am return the number okay so what we want to do is we want to approach this a little bit more dynamically and let's say well let's run this off I submit a number a little bigger I'll get a number I submit a word I get a word right so I want to replicate the same behavior so we'll stay with the word here and let's talk about how do you actually create lambda expressions right or expressions themselves so lambda is one type of expressions and then there multitude of other expressions right so kind of how you have the type class in a reflection to go into the reflection world and start reading information about a specific type an expression what in in the expression world what you have is the expression class okay so with the expression class you can it's a static class where it which has static functions which most of these functions what they will do is they will return some kind of expression go to expression if you want one a convert expression convert checked expression continue and add expression so if you want to perform an add operation and add an assign these are your expressions which and this is how you create them okay so going back to this simple overview what I want to do is I kind of want to go over this expression and redraw it in the node notation so we know what we're creating and that will really help you braid the expression if you understand how to form the tree structure okay so let's go ahead comment this out we will go to just the expression and we will just dump this so if we want to create this expression this is what we will do and just make it a little bit simpler I will actually grab the name write shorten it a little bit so at the root and usually the root is the last thing that needs to be invoked right the last thing that needs to be actioned on so it's at the top case it's the root so going back to the paint at the root we have the lambda a so this is my L so we have the lambda expression we have parameters right so parameters this is not a binary tree these are just some nodes on lambda object on the lambda note so here we have parameters and parameters can have a bunch of a bunch of things so parameters themselves they have lists of type so if we let's say if we add an integer here and we add another parameter to the function here this list is gonna increment with every parameter so in here what we have is the user so say e user and really need to type it out but yeah and any other parameter adds a branch now the next thing is the body so we have the body and again it's not too much to concern yourself here the body is just another expression and the primary expression that is gonna be holding is the property expression and the property expression is a node type of member access so the operation that it performs kind of like when we have the add operation right so when we had a node where we preferred performed an ad or a multiply these this operation I'll just say that it's a dot because that's kind of how we denote it here in the syntax we put a dot access something so this is s1 say I'm gonna stay - dot right what's on the left hand side right what's on the right hand side or rather what are we selecting so we are selecting a name so this will be on your right hand side not sure if it's right hand side exactly this doesn't look like a binary tree so anyway I'll just put the name here and the you understand that this is the user okay so and then yeah as the tree gets traversed something along the lines of this always left to right so yeah so now you can see that essentially before it's kind of like dependency injection we were you need to know about all of your dependencies before you can bunch them up or before you can fill the container so before we can construct the lambda we need to know about our parameters before we can construct the lambda body we need to know what the body consists of right so what we will do is we start with the bottom notes and we put it on the top higher note and then on the higher note until we end up with a lambda okay so let's go back to the grading expression and what we're after here is this selector right so what we're what we want to do is we want to produce a lambda that will perform the selection operation of a specific field or property and let us basically grab the value right because that's what the selector is gonna do right if we put a while unit it's gonna run the selector it and it's gonna give us back a value it's kind of like just having a function that will that has a dynamic selector based another parameter nevertheless how do we do this so before we can basically access the name on the user we need to know about the user so we need a parameter right so we need the left hand side first before we do the right hand side so parameter first let's go ahead and [Music] right a parameter right we're gonna create a parameter it needs a type what type do we deal with some class all right so type off some class some bus barometer let's just call parameter I feel like that is just gonna be simpler so yeah it's a whole if you again if you like it and not sure why I'm saying again but if you imagine this box where you can put the circle bits the square bits where we have a function is like a thing with no holes and then as soon as we create a parameter we're opening up a hole that it's like that's some class size so you only put some class sizes and there right so what we essentially here what we've done here is we've created this bit here so not not specifically the parameter types but the user type we will you will see how we are gonna be put when we're creating the lambda you will see how we're going to be essentially populating the lambdas parameter okay so once we have the user we can then go ahead and access the user's name right so we want the access ur and if we remember correctly it's called the member access so let's go ahead and write to find some kind of member accessibility expression member access make member access and not white that's and rather what we want is property or field okay so creates a member expression that represents accessing or property or field right so you can see how the names don't match up here this is why under and it's important to understand what you're doing being able to speak it because if you can say what you want to do and you don't know how to express it in c-sharp it becomes very easy to sort search for this problem in google right so you can then express your question in the search bar and you get a response from a Stack Overflow or whatever so naming is hard it's a pardonable mistake for creators of c-sharp right so that's okay properly your field is still pretty explainable if you use it a lot you're gonna remember it so properly your field what do we have error and let's go ahead and f12 on this if it's gonna let me so here we have expression and string the expression is what are we selecting it on and then the string is property or field so the parameter that we're trying to select it from and what are we selecting well this is the interesting part we can just go ahead and say select this property okay and at this point what we have is this part so now at the we don't necessarily need to create as parameter and body blocks they'll not block so sorry nodes they live on the lambda node or our kind of part of the lambda class kind of like when I was showing you the tree the numbers or the actual values belonged to the expression anything right so let's call this accessor next thing we want is the lambda right so lambda equals expression lambda and this one can be tricky there's a lot of things here and so next yeah so you can see there are a lot of overloads again you will kind of need to understand well google your way around maybe sometimes so here on the right if you're using Visual Studio or anything you're gonna be able to reach this with f12 by inspecting the implementation or whatever so here you can see the body is where we're promised cailli supplying the body and the parameter so the that's what it's going to fill those two nodes well yeah let's go ahead and fill those out before that tail call I'm not sure we'll need that so body is accessor parameter let's call safe false here and parameter so this is a lambda this is a lambda expression at the end of the day what can we do with the lambda expressions well you can compile them what can we do then well we can invoke them and we don't have a specific invoke here we get a dynamic invoke where we can then supply our objects here and look up I'm not sure if we can do this let me just select the property select property rather now on this that was none we the Select this is the property that we want to select where we want to select it from some class let's grab it from some class let's see if we can dump this I don't think this is the thing nice no okay so yeah dynamic invoke instead then there's a way the way around so yeah let's see what we get and we get two hello worlds if we swap these around we then select the numbers okay so here you can see the the solution hard-coded not scalable what do we do we are then creating a selector using the expression API and yeah this is a this is data before we compile it you can compile it store the function so this is cacheable there are ways to approach this to make it even faster kind of like one-time setup thing so yeah so there is a lot of interesting stuff that you can do with this as you can see a lot of popular frameworks use this and hopefully this video helped you understand the concept remember the code you write is data and data is about okay that's the primary concept that allows you to think of solutions to problems using the reflection and the expression tree API is a yeah if you did like this video leave a like subscribe if you have any questions make sure to leave them in the comment section I stream on Wednesdays and Sundays so if you're interested in that don't forget to join the discord Channel I do a lot of updates there and as always hopefully I'll see you in my other videos
Info
Channel: Raw Coding
Views: 16,913
Rating: undefined out of 5
Keywords: c#, asp.net core, tutorial, c# tutorial, asp.net core tutorial, explained, example, expressions, expression tree, expression, what are expression trees?, how to use expression trees?, how to read expression trees?, how to create expression trees?, read, create
Id: dwr40KytyaY
Channel Id: undefined
Length: 36min 43sec (2203 seconds)
Published: Sun May 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.