Welcome to Shorpy

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.

GitHub Repository Nuget Package

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.

Installation

Install via NuGet Package Manager:

Install-Package Shorpy

Or via .NET CLI:

dotnet add package Shorpy

Getting Started

All you need to get-started are two configuration files; per each Entity you wish to bind with Shorpy's wonderful functionality.

  1. SnSAble configuration file
    • An SnSAble is a class that defines how to access all Searchable and Sortable properties of an Entity
    • An 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;
    }
      
    
  2. Includeable configuration file
    • An Includeable is a class that defines how to access all Navigation properties or Related items of an Entity
    • An 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;

  1. PaginateModel is a DTO coming from the Shorpy package.
  2. You must pass your DBContext to Shorpy

Example

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
  }
          
      

Hope you enjoy using Shorpy

If you "Star" the GitHub Repository, you will get notified of new updates and patches.