Shorpy is a powerful and easy-to-use .NET library that provides seamless dynamic Pagination, Searching, and Sorting for applications using Entity Framework Core, ASP.NET Core WebAPI, and written in C#. It aims to simplify data handling and improve performance by offering efficient querying mechanisms.
Implementing pagination, dynamic search, and sorting in C# with
Entity Framework Core is often a tedious and repetitive task. Each
time you need these features in a new project, you have to write
almost the same boilerplate code, adjusting it slightly to fit
different models and requirements. While EF Core provides some
basic support through methods like .Skip()
and
.Take()
for pagination or .OrderBy()
for
sorting, developers still have to manually combine these
operations, handle edge cases, and ensure efficiency. This process
becomes even more cumbersome when dealing with dynamic queries
where filters and sort orders can change at runtime.
One of the major pain points is writing the logic to construct
dynamic LINQ queries based on user input. Implementing a flexible
and efficient search mechanism requires building
IQueryable
expressions dynamically, often using
Expression Trees
or PredicateBuilder
,
which can be complex and error-prone. Similarly, supporting
multiple sorting fields dynamically means handling string-based
property names, validating inputs, and applying order operations
correctly. Every time you implement these features, you find
yourself rewriting or copy-pasting the same logic across different
projects, which is both time-consuming and inefficient.
To further complicate matters, pagination requires careful handling to optimize database queries, ensuring that large datasets don’t degrade performance. While libraries like AutoMapper and custom DTOs can simplify some of these tasks, setting them up still takes additional effort. Given how often these features are needed in applications, a reusable abstraction or library would significantly reduce redundancy.
This is where Shorpy can save a lot of time and effort. Instead of manually implementing pagination, dynamic search, and sorting in every project, Shorpy provides a ready-made, efficient solution that simplifies these repetitive tasks.
With Shorpy, you don’t have to worry about constructing complex
IQueryable
expressions, handling dynamic filtering, or
ensuring optimal database queries. It abstracts away the boilerplate
code, allowing developers to focus on business logic rather than
spending hours writing and debugging common features. This not only
speeds up development but also improves maintainability by enforcing
consistency across projects.
Install via NuGet Package Manager:
Install-Package Shorpy
Or via .NET CLI:
dotnet add package Shorpy
All you need to get-started are two configuration files; per each Entity you wish to bind with Shorpy's wonderful functionality.
SnSAble
configuration fileSnSAble
is a class that defines how to access all
Searchable and Sortable properties of an Entity
SnSAble
implements ISnSAble
interface
Imagine you have two Entities called Employee
&
Department
public class Employee
{
public int Id {get;set;}
public string Name {get;set;} = null!;
public string Address {get;set;} = null!;
public bool IsActive {get;set;}
public int Department {get;set;}
public virtual Department DepartmentNavigation {get;set;}
}
public class Department
{
public int Id {get;set;}
public string Name {get;set;} = null!;
public virtual List<Employee> Employees {get;set;} = new();
}
Now, if you only want to allow the users to search or sort by, let's
say the Name
, IsActive
properties & the
Department.Name
property of an Employee
,
your SnSable
for the Employee
will look
something like the following;
// Define the type as reusable to improve readability
using System;
using System.Linq.Expressions;
public class SnSEmployee : ISnSAble
{
public static readonly Expression<Func<Employee, object>> Name = e => e.Name;
public static readonly Expression<Func<Employee, object>> IsActive = e => e.IsActive;
public static readonly Expression<Func<Employee, object>> DepartmentName = e => e.DepartmentNavigation.Name;
}
Includeable
configuration fileIncludeable
is a class that defines how to access
all Navigation properties or Related items of an
Entity
Includeable
implements
IIncludeable
interface
For the above Employee
entity, the
Includeable
will look similar to the following
using type = Expression<Func<Employee, object>>;
public class IncEmployee: IIncludable
{
public static readonly type Department = e => e.DepartmentNavigation;
}
That's it! You are good to go. All you have to do now is handle the pagination request
Assuming you are handling the pagination request from a controller; your controller will look similar to the following;
using Shorpy.DTOs;
using Shorpy.Paginate;
[HttpPost]
public virtual async Task<IActionResult> Paginate(PaginateModel pm)
{
// you must pass your DBContext to the paginate function
var paginatedObj = await Paginate<Employee, SnSEmployee, IncEmployee>.PaginateWithTracking(_dbcontext, pm);
return Ok(paginatedObj);
}
Two important things to note here;
PaginateModel
is a DTO coming from the
Shorpy
package.
DBContext
to Shorpy
Below is an example of a request to paginate and search for all
Employees
whose name includes "Tim" and include the
Department
navigation
curl -X POST /api/employees/paginate
-H "Content-Type: application/json"
-d '{
"search": [
{
"field": "name",
"value": "Tim",
"method": "like"
}
],
"include": ['Department'],
"limit": 10,
"offset": 0
}'
The above request will give the following response
{
"values": [
{
"id": 1,
"name": "Timothée Bing",
"address": "xxx xxx xxxx",
"isActive": 1,
"departmentNavigation": {
"id": 2,
"name": "HR"
}
}
],
"total": 1
}
If you "Star" the GitHub Repository, you will get notified of new updates and patches.