Sorting in WebAPI - a generic way to apply sorting
In
this post you will learn how to implement sorting functionality in WebAPI so
that client can ask for sorted data by sending columns names through query
string. Client can send more than one short parameters and request for ascending
or descending data in response. Sorting functionality should be generic enough so
that we can reuse it.
Data source
So, let's get started. First of all, let's look at the sample data
we will be using to demo this functionality.
public class StudentController : ApiController
{
// Data source
List<Student> db = new List<Student>();
public StudentController()
{
db
= new List<Student>
{
new Student { Id = 1, Name = "Abhimanyu K Vatsa", City = "Bokaro" },
new Student { Id = 2, Name = "Deepak Kumar Gupta", City = "Bokaro" },
new Student { Id = 3, Name = "Manish Kumar", City = "Muzaffarpur" },
new Student { Id = 4, Name = "Rohit Ranjan", City = "Patna" },
new Student { Id = 5, Name = "Shiv", City = "Motihari" },
new Student { Id = 6, Name = "Rajesh Singh", City = "Dhanbad" },
new Student { Id = 7, Name = "Staya", City = "Bokaro" },
new Student { Id = 8, Name = "Samiran", City = "Chas" },
new Student { Id = 9, Name = "Rajnish", City = "Bokaro" },
new Student { Id = 10, Name = "Mahtab", City = "Dhanbad" },
};
}
// rest code goes here
}
In above code you can see we have a very simple student object
with pre-populated data through constructor. You may have other data sources or
you may be using some sort of repository, in either case you are good.
Back to basics
Now, let's go ahead and write a very simple GET method which will
return all the data.
public IHttpActionResult Get()
{
return Ok(db);
}
So, above method will return all the records with status code
200 OK, here's the output:
You might notice, chrome renders XML data by default and in
same case IE asks to save JSON file. Basically, WebAPI can send JSON as
well as XML repose and we can configure it. In my case, I just
removed XML support which made JSON as output on Chrome (above screenshot).
Notice, I also reformatted the JSON response. To do this I used following code:
Now, let's move to our original discussions.
Implementing field level data selection
I'm going to add this functionality in above/existing GET method.
So our client will pass comma separated column names using query string
variable sort. The client request URLs will look following:
1. http://localhost:36140/api/student?sort=id
(order by id)
2. http://localhost:36140/api/student?sort=-id
(order by id descending)
3. http://localhost:36140/api/student?sort=id,name
(order by id then by name)
Notice - sign is being used for descending sort. If client does
not pass query string variable sort, service should return the
default order from database. So, here's the new GET method with all said
functionality.
public IHttpActionResult Get(string sort = "id")
{
//
Convert data source into IQueryable
//
ApplySort method needs IQueryable data source hence we need to convert it
// Or we
can create ApplySort to work on list itself
var data = db.AsQueryable();
// Apply
sorting
data = data.ApplySort(sort);
//
Return response
return Ok(data);
}
Now
let’s understand above code point by point.
1.
sort
is a parameter of GET method, which will be assigned from query string value
2. sort
parameter’s default value is id (id column) so in case no query string is
supplied, the default will be used
3. My data source is list so I converted it into
IQueryable and there are two major reasons behind this. First, I will be
writing ApplySort method in the next line which is a generic method to work
with any kind of collection or type. Second, after sorting you will have
opportunity to send dependent records to client aka eager and lazy loading concepts. It’s totally up to you how
you take it forward but service should be entirely generic.
4. Once
we have data source and sort string (from query string), we can pass it to our
generic method which we will see next.
In
above code I’m using ApplySort generic method, here’s the code:
public static class IQueryableApplySortExtension
{
public static IQueryable<T> ApplySort<T>(this IQueryable<T> source, string strSort)
{
if(source == null)
{
throw new ArgumentNullException("source", "Data source is
empty.");
}
if(strSort == null)
{
return source;
}
var lstSort = strSort.Split(',');
string sortExpression = string.Empty;
foreach(var sortOption in lstSort)
{
if(sortOption.StartsWith("-"))
{
sortExpression =
sortExpression + sortOption.Remove(0, 1) + "
descending,";
}
else
{
sortExpression =
sortExpression + sortOption + ",";
}
}
if(!string.IsNullOrWhiteSpace(sortExpression))
{
//
Note: system.linq.dynamic NuGet package is required here to operate OrderBy on
string
source =
source.OrderBy(sortExpression.Remove(sortExpression.Count() - 1));
}
return source;
}
}
Now let’s
understand code point by point:
1. It’s a generic method accepting data source
and sort string in parameters.
2. It checks data source or sort string should
not be empty.
3. If data source and sort string are not empty
then this splits the sort string into lstSort
array and then loops on each item in the array. After that a new variable sortExpression is defined and assigned
empty string.
4. Inside foreach loop, if condition will check
if sort string item has - sign, if yes then this will apply sort in descending
order and if no then default which is ascending will work. In case of descending
I’m adding extra string ‘descending’ in sortExpression.
Also, removing the - part from string, because this is not required in data query.
5. Once
we have sortExpression assigned inside
foreach loop, we will check if this is still null or white space. If not, then
source (records) will be re-assigned with new order.
6.
Notice the commented message, by default OrderBy expression accepts System.Linq.Expressions.Expression
type:
But we
need this OrderBy accepts string type because we have sort conditions in sortExpression which is of string type.
In order to do this, we need to install a very useful NuGet package which been
around long time that is System.Linq.Dynamic. Let’s install that to add extra
power in OrderBy expression.
Once
this package is installed, add a new namespace using System.Linq.Dynamic; and you are good to go. Now you will see
following:
7. Notice in the body of OrderBy expression I’m
removing the leading comma sign from sortExpression.
8. Once
OrderBy is applied, I am re-assigning source variable with sorted data and
sending it back to WebAPI action which will return data with 200 OK status
code.
Here’s
the running application screenshots:
In case
you would like to try out this project, I published
the code on GitHub.
Hope
this helps.
Comments
Post a Comment