Pagination, Sorting and Filtering with LINQ
yawo
December 26, 2023
Here is our sample data:
var books = new List<Book>(){
new Book
{
Id = 1,
ISBN = "EWRE23453FVFQ",
Title = "First Book title",
Description = "Description",
Pages = 299,
Author = new Author { Name = "John Doe", Bio = "John's Bio"}
},
new Book
{
Id = 2,
ISBN = "UYTRE5434556",
Title = "Second Book title",
Description = "Description",
Pages = 322,
Author = new Author { Name = "Albert David", Bio = "David's Bio" }
},
new Book
{
Id = 3,
ISBN = "654RDBSDB445",
Title = "Third Book title",
Description = "Description",
Pages = 99,
Author = new Author { Name = "Sun Micro", Bio = "Sun's Bio" }
}
};
public class Book
{
public int Id { get; set; }
public string ISBN { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public required Author Author { get; set; }
public int Pages { get; set; }
}
public class Author
{
public string Name { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty;
}
Pagination
Here is a controller action method that shows how the page parameter is used ti achieve pagination
[HttpGet]
public async Task<IActionResult> GetBooks(int? page)
{
IQueryable<Book> query = books.AsQueryable();
int pageSize = 2;
if (page == null || page < 1) page = 1;
int totalPages = 0;
decimal count = query.Count();
totalPages = (int)Math.Ceiling(count / pageSize);
if (page > totalPages)
{
page = 1;
}
query = query.Skip((int)(page - 1) * pageSize).Take(pageSize);
var _books = query.ToList();
var response = new
{
Page = page,
TotalPages = totalPages,
PageSize = pageSize,
Data = _books
};
return Ok(response);
}
Testing the requests:
https://localhost:7192/api/Books
https://localhost:7192/api/Books?page=1
{
"page": 1,
"totalPages": 2,
"pageSize": 2,
"data": [
{
"id": 1,
"isbn": "EWRE23453FVFQ",
"title": "First Book title",
"description": "Description",
"author": {
"name": "John Doe",
"bio": "John's Bio"
},
"pages": 299
},
{
"id": 2,
"isbn": "UYTRE5434556",
"title": "Second Book title",
"description": "Description",
"author": {
"name": "Albert David",
"bio": "David's Bio"
},
"pages": 322
}
]
}
Filtering
We will be filtering using title and author name. author and title are optional parameters
// author and title are optional parameters
IQueryable<Book> query = books.AsQueryable();
if(author != null)
{
query = query.Where(
x => x.Author.Name.ToLower()
.Contains(author.ToLower()));
}
if (title != null)
{
query = query.Where(
x => x.Title.ToLower()
.Contains(title.ToLower()));
}
Sorting
sort and order are optional parameters
// sort and order are option parameters
IQueryable<Book> query = books.AsQueryable();
if (sort == null) { sort = "id"; }
if (order == null || order != "asc") { order = "desc"; }
if (sort.ToLower() == "id")
{
if (order == "asc")
{
query = query.OrderBy(b => b.Id);
}
else
{
query = query.OrderByDescending(b => b.Id);
}
}
if (sort.ToLower() == "title")
{
if (order == "asc")
{
query = query.OrderBy(b => b.Title);
}
else
{
query = query.OrderByDescending(b => b.Title);
}
}
Final Controller Action
[HttpGet]
public async Task<IActionResult> GetBooks(int? page,
string? author, string? title, string? sort, string? order)
{
IQueryable<Book> query = books.AsQueryable();
// FILTERING BEGINS
if(author != null)
{
query = query.Where(
x => x.Author.Name.ToLower()
.Contains(author.ToLower()));
}
if (title != null)
{
query = query.Where(
x => x.Title.ToLower()
.Contains(title.ToLower()));
}
// FILTERING ENDS
// SORTING BEGINS
if (sort == null) { sort = "id"; }
if (order == null || order != "asc") { order = "desc"; }
if (sort.ToLower() == "id")
{
if (order == "asc")
{
query = query.OrderBy(b => b.Id);
}
else
{
query = query.OrderByDescending(b => b.Id);
}
}
if (sort.ToLower() == "title")
{
if (order == "asc")
{
query = query.OrderBy(b => b.Title);
}
else
{
query = query.OrderByDescending(b => b.Title);
}
}
// SORTING ENDS
// PAGINATION BEGINS
int pageSize = 2;
if (page == null || page < 1) page = 1;
int totalPages = 0;
decimal count = query.Count();
totalPages = (int)Math.Ceiling(count / pageSize);
if (page > totalPages)
{
page = 1;
}
query = query.Skip((int)(page - 1) * pageSize).Take(pageSize);
// PAGINATION ENDS
// GETTING THE FINAL RESULT
var _books = query.ToList();
var response = new
{
Page = page,
TotalPages = totalPages,
PageSize = pageSize,
Data = _books
};
return Ok(response);
}
Requests and Responses
Filtering
https://localhost:7192/api/Books?author=john
{
"page": 1,
"totalPages": 1,
"pageSize": 2,
"data": [
{
"id": 1,
"isbn": "EWRE23453FVFQ",
"title": "First Book title",
"description": "Description",
"author": {
"name": "John Doe",
"bio": "John's Bio"
},
"pages": 299
}
]
}
Sorting
https://localhost:7192/api/Books?page=1&sort=title&order=desc
{
"page": 1,
"totalPages": 2,
"pageSize": 2,
"data": [
{
"id": 3,
"isbn": "654RDBSDB445",
"title": "Third Book title",
"description": "Description",
"author": {
"name": "Sun Micro",
"bio": "Sun's Bio"
},
"pages": 99
},
{
"id": 2,
"isbn": "UYTRE5434556",
"title": "Second Book title",
"description": "Description",
"author": {
"name": "Albert David",
"bio": "David's Bio"
},
"pages": 322
}
]
}