In this video, Nate shows you how to build a sort expression dynamically based on the parameters provided by the client.
- [Instructor] Adding sort to a query with LINQ is as simple as adding methods like query.OrderBy().ThenByDescending, and so on. Unfortunately, this only works if you know the sort criteria at compile time. We need to apply different sort criteria depending on the query string of the request, which is a little trickier. To keep the sort options model light, we'll delegate this responsibility to SortOptionsProcessor as well. We'll simply, from this method, return_processor.Apply and delegate this.
Over in the SortOptionsProcessor, we'll need to add this apply method. This will be a method that returns an IQueryable of TEntity, and it also takes an IQueryable of TEntity. It's important to use IQueryable here and not IEnumerable. IQueryable contains an expression tree that can be transformed into a database query statement like SQL. By adding to that expression tree, we can dynamically change the query at run time. Inside of the apply method, first we need to get all the valid terms from the query.
We'll call GetValidTerms and put that into an array. If there aren't any terms, we can simply reflect the query back and we're done. We need to declare a variable to hold onto the modifiedQuery because we may modify it more than once. We also need to keep track of how many times we've called OrderBy. If we've called it more than once, we need to call ThenBy. We'll keep track of it in this Boolean. Now, we can iterate over each term with a foreach.
I've added a class called ExpressionHelper, which contains code that'll make it easier to build up these expression trees. If you're following along at home, you can copy this file from the Exercise Files begin state of this video. The first thing we need to do is get the propertyInfo of the property this term refers to. We can call ExpressionHelper.GetPropertyInfo of TEntity, and pass in term.Name. This looks up that property on the entity object. We also need to get a reference to the entity object, so we'll say var object = ExpressionHelper .Parameter<TEntity>.
This is a generic reference to that entity object. Now we need to build up the LINQ expression, but we're building it up backwards. Remember, ultimately, our goal is to do something like query = query.OrderBy and then (x => x.Property). That's our goal, and first, we'll build this part, x => x.Property. We can get the property access expression by saying var key = ExpressionHelper.GetPropertyExpression, and we'll pass in the entity object and the propertyInfo.
Then we'll get the entire expression, which is known as a keySelector, by saying ExpressionHelper.GetLambda, we'll pass in the type of TEntity, the propertyInfo.PropertyType, the object and the property or the key. This will construct this expression here. Next, we need to call the OrderBy or the ThenBy method. We need to do something like query.OrderBy or ThenBy, and we may also need to call Descending, if the term needs to be descending.
Then as a parameter to that method call, we need to pass in this expression that we already built, which is x => x.Property. We want to apply this method call to the modified query object, so we'll say modifiedQuery = ExpressionHelper .callOrderByOrThenBy, and we'll pass the modifiedQuery, the useThenBy flag, whether the term is supposed to be Descending, the PropertyType, and finally the keySelector.
For the next iteration, we need to make sure that useThenBy is true. After we're done with the loop, we can simply return the modifiedQuery. All right, that's a lot of expression code, but that should do the trick. Let's test it out. I can resend the request I have in POSTMAN, which is OrderBy = rate. This should order the rooms by their rate. What I get back is the collection of rooms ordered by their rate or price. These items are sorted in ascending order by default. But let's try to switch that up.
If we tell it to be sorted descending, we should see the exact opposite, so now the more expensive room is on top. We can also try ordering by name. If we remove the descending flag, we should see the opposite sort. All right, now the database query is being dynamically updated based on the parameters passed to the route.
- REST vs. RPC
- Using HTTP methods (aka verbs)
- Returning JSON
- Creating a new API project
- Building a root controller
- Routing to controllers with templates
- Requiring HTTPS for security
- Creating resources and data models
- Returning data and resources from a controller
- Representing links (HREFs)
- Representing collections
- Sorting and searching collections
- Creating forms
- Caching and compression
- Authentication and authorization for RESTful APIs