ToolBox.EntityFramework 2.4.0

dotnet add package ToolBox.EntityFramework --version 2.4.0
                    
NuGet\Install-Package ToolBox.EntityFramework -Version 2.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ToolBox.EntityFramework" Version="2.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ToolBox.EntityFramework" Version="2.4.0" />
                    
Directory.Packages.props
<PackageReference Include="ToolBox.EntityFramework" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ToolBox.EntityFramework --version 2.4.0
                    
#r "nuget: ToolBox.EntityFramework, 2.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ToolBox.EntityFramework@2.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ToolBox.EntityFramework&version=2.4.0
                    
Install as a Cake Addin
#tool nuget:?package=ToolBox.EntityFramework&version=2.4.0
                    
Install as a Cake Tool

ToolBox.EntityFramework.Filters

A lightweight, high-performance library for dynamic filtering, sorting, and pagination with Entity Framework Core.

Features

โœจ Dynamic Filtering - Build complex filter expressions with AND/OR logic
๐Ÿ”„ Multi-Column Sorting - Sort by multiple properties with direction control
๐Ÿ“„ Smart Pagination - Efficient server-side pagination with rich metadata
โšก Performance Optimized - Reflection caching and AsNoTracking() support
๐ŸŽฏ FluentValidation Ready - Built-in validators included
๐Ÿงช Type-Safe - Expression trees that translate directly to SQL
๐Ÿ”ง ASP.NET Core Integration - Extension methods and DI registration

Quick Start

Basic Pagination

var request = new PageRequest(pageNumber: 1, pageSize: 20);
var result = await dbContext.Products.ToPageAsync(request);

Console.WriteLine($"Page {result.PageNumber}/{result.TotalPages}");
Console.WriteLine($"Total Items: {result.TotalItems}");

With Sorting

var request = new PageRequest(1, 20)
{
    Sorts = new[]
    {
        new SortDescriptor { PropertyName = "Name", Direction = SortDirection.Ascending },
        new SortDescriptor { PropertyName = "CreatedDate", Direction = SortDirection.Descending }
    }
};

var result = await dbContext.Products.ToPageAsync(request);

With Filtering

var request = new PageRequest(1, 20)
{
    Filters = new[]
    {
        new FilterGroup
        {
            Filters = new[]
            {
                new ExpressionFilter 
                { 
                    PropertyName = "Price", 
                    ValueString = "50", 
                    Comparison = Comparison.GreaterThan 
                },
                new ExpressionFilter 
                { 
                    PropertyName = "Category", 
                    ValueString = "Electronics", 
                    Comparison = Comparison.Equal 
                }
            },
            FilterAssociation = FilterAssociation.And
        }
    }
};

var result = await dbContext.Products.ToPageAsync(request);

Complete Example

var request = new PageRequest(1, 20)
{
    Filters = new[]
    {
        new FilterGroup
        {
            Filters = new[]
            {
                new ExpressionFilter { PropertyName = "Price", ValueString = "100", Comparison = Comparison.GreaterThan },
                new ExpressionFilter { PropertyName = "InStock", ValueString = "true", Comparison = Comparison.Equal }
            },
            FilterAssociation = FilterAssociation.And
        }
    },
    Sorts = new[]
    {
        new SortDescriptor { PropertyName = "Name", Direction = SortDirection.Ascending }
    }
};

var result = await dbContext.Products.ToPageAsync(request);

Supported Comparisons

Comparison Description Supported Types
Equal Exact match All types
NotEqual Not equal All types
GreaterThan Greater than Numeric, DateTime
GreaterThanOrEqual Greater than or equal Numeric, DateTime
LessThan Less than Numeric, DateTime
LessThanOrEqual Less than or equal Numeric, DateTime
Contains Contains substring String only
StartsWith Starts with String only
EndsWith Ends with String only

ASP.NET Core Integration

Program.cs

using ToolBox.EntityFramework.Filters;

var builder = WebApplication.CreateBuilder(args);

// Register validators and services
builder.Services.AddToolBoxFilters();

var app = builder.Build();

Controller Usage

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly AppDbContext _context;

    public ProductsController(AppDbContext context)
    {
        _context = context;
    }

    [HttpPost("search")]
    public async Task<ActionResult<PageOf<Product>>> Search(
        [FromBody] PageRequest request,
        CancellationToken cancellationToken)
    {
        // Automatic validation with FluentValidation
        var result = await _context.Products
            .ToPageAsync(request, cancellationToken);
            
        return Ok(result);
    }
}

API Request Example

POST /api/products/search
{
  "pageNumber": 1,
  "pageSize": 20,
  "filters": [
    {
      "filters": [
        {
          "propertyName": "Price",
          "valueString": "50",
          "comparison": 1,
          "filterAssociation": 0
        },
        {
          "propertyName": "Category",
          "valueString": "Electronics",
          "comparison": 0,
          "filterAssociation": 0
        }
      ],
      "filterAssociation": 0
    }
  ],
  "sorts": [
    {
      "propertyName": "Name",
      "direction": 0
    }
  ]
}

API Response Structure

{
  "pageNumber": 1,
  "pageSize": 20,
  "totalItems": 156,
  "totalPages": 8,
  "hasPreviousPage": false,
  "hasNextPage": true,
  "items": [
    {
      "id": 1,
      "name": "Product A",
      "price": 99.99,
      "category": "Electronics"
    }
  ]
}

Advanced Features

Complex Filter Logic

Combine multiple filter groups with AND/OR logic:

var filters = new[]
{
    new FilterGroup
    {
        // (Price > 100 AND Category = "Electronics")
        Filters = new[]
        {
            new ExpressionFilter { PropertyName = "Price", ValueString = "100", Comparison = Comparison.GreaterThan },
            new ExpressionFilter { PropertyName = "Category", ValueString = "Electronics", Comparison = Comparison.Equal }
        },
        FilterAssociation = FilterAssociation.And
    },
    new FilterGroup
    {
        // OR (FeaturedProduct = true)
        Filters = new[]
        {
            new ExpressionFilter { PropertyName = "FeaturedProduct", ValueString = "true", Comparison = Comparison.Equal }
        },
        FilterAssociation = FilterAssociation.Or
    }
};

SQL Output:

WHERE (Price > 100 AND Category = 'Electronics') OR (FeaturedProduct = 1)
ORDER BY Name ASC
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY

Case-Insensitive Property Names

Property names are matched case-insensitively:

// All of these work
new SortDescriptor { PropertyName = "Name" }
new SortDescriptor { PropertyName = "name" }
new SortDescriptor { PropertyName = "NAME" }

Validation

Built-in FluentValidation validators ensure:

  • Page number โ‰ฅ 1
  • Page size between 1-100 (configurable)
  • Valid property names
  • Non-empty filter values
  • Valid comparison operators

Performance

  • Reflection Caching: PropertyInfo lookups cached in ConcurrentDictionary
  • Single Query: Filters and sorts translated to single SQL query
  • No Tracking: AsNoTracking() applied automatically
  • Expression Trees: Direct translation to SQL (no in-memory filtering)

Configuration

Custom Page Size Limits

public class PageRequest
{
    private const int MaxPageSize = 100; // Modify this constant
    // ...
}

Clear Reflection Cache

// Useful for testing or hot reload scenarios
SortBuilder.ClearCache();
ExpressionBuilder.ClearCache();

API Reference

PageRequest

Property Type Default Description
PageNumber int 1 Current page (1-based)
PageSize int 10 Items per page (max: 100)
Filters IEnumerable<FilterGroup> null Filter groups
Sorts IEnumerable<SortDescriptor> null Sort descriptors

PageOf<T>

Property Type Description
PageNumber int Current page number
PageSize int Items per page
TotalItems int Total items across all pages
TotalPages int Total number of pages
HasPreviousPage bool True if previous page exists
HasNextPage bool True if next page exists
Items ICollection<T> Items in current page

Extension Methods

// IQueryable<T> extensions
Task<PageOf<T>> ToPageAsync(PageRequest request, CancellationToken cancellationToken = default)
Task<PageOf<T>> ToPageAsync(int pageNumber, int pageSize, ...)

Requirements

  • .NET 10.0+
  • Entity Framework Core 9.0+
  • FluentValidation 11.0+ (optional, for validation)

Error Handling

The library provides detailed error messages:

// Property not found
Property 'InvalidName' not found on type 'Product'. Available properties: Id, Name, Price, Category

// Invalid type for string operation
Contains comparison is only supported for string properties. Property 'Price' is of type 'Decimal'.

// Empty property name
Sort property name cannot be empty.

Best Practices

  1. Use AsNoTracking() - Already applied automatically for read queries
  2. Index filtered/sorted columns - Add database indexes on frequently queried properties
  3. Limit page size - Default max is 100 items per page
  4. Validate input - Use provided FluentValidation validators
  5. Cache property lookups - Handled automatically via ConcurrentDictionary

Performance Tips

// โœ… Good - Single query
var result = await dbContext.Products
    .Include(p => p.Category)
    .ToPageAsync(request);

// โŒ Bad - Multiple queries
var products = await dbContext.Products.ToListAsync(); // Loads everything
var filtered = products.Where(...); // In-memory filtering
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.4.0 117 2/6/2026
2.3.0 93 2/6/2026
1.1.0 90 2/5/2026