PepperX.QueryForge
2.1.0
dotnet add package PepperX.QueryForge --version 2.1.0
NuGet\Install-Package PepperX.QueryForge -Version 2.1.0
<PackageReference Include="PepperX.QueryForge" Version="2.1.0" />
<PackageVersion Include="PepperX.QueryForge" Version="2.1.0" />
<PackageReference Include="PepperX.QueryForge" />
paket add PepperX.QueryForge --version 2.1.0
#r "nuget: PepperX.QueryForge, 2.1.0"
#:package PepperX.QueryForge@2.1.0
#addin nuget:?package=PepperX.QueryForge&version=2.1.0
#tool nuget:?package=PepperX.QueryForge&version=2.1.0
![]()
PepperX.QueryForge
What this is
PepperX.QueryForge is the abstract core of the QueryForge ecosystem: the shared vocabulary for describing "filter this, sort it like that, page it, maybe group it" — as a plain C# object or a plain JSON payload — without tying that intent to any particular database or library.
It defines the models, a fluent builder, and a validation engine that every provider builds on top of. It runs no SQL and has no dependencies.
It does, however, ship one execution engine — the in-memory one, since running a Query over an
IEnumerable<T> needs nothing external:
var result = products.ToQueryResult(query); // same QueryResult<T> every provider returns
To run a
Queryagainst a database, add a provider:PepperX.QueryForge.Dapperfor parameterized SQL over Dapper (SQL Server, PostgreSQL, MySQL/MariaDB, Oracle, SQLite), orPepperX.QueryForge.EFCoreto have Entity Framework Core generate the SQL from your model.
At a glance
- 🧱 One shape,
Query— filters, sorting, paging, and grouping, whether it comes from C# or from a client as JSON. - 🔍 Nested filter logic —
AND/OR/AND NOT/OR NOTgroups, any depth. - 🌳 Grouping models — describes multi-level
key / count / itemshierarchies for providers to build. - 🛡️ Validation engine — silently strip or hard-block invalid columns/paging, shared by every provider.
- 🔌 Provider-agnostic — write the intent once, plug in whichever execution engine you need.
Install
dotnet add package PepperX.QueryForge
(You'll rarely install this directly — it comes along with whichever provider you pick.)
The Query object
public class Query
{
public QueryCriteria Criteria { get; set; } // filters
public QueryPaging Paging { get; set; } // { Size, Number }
public IReadOnlyList<string> SelectColumns { get; set; } // projection
public IReadOnlyList<SortDescriptor> SortColumns { get; set; } // ORDER BY
public IReadOnlyList<GroupByDescriptor> GroupByColumns { get; set; } // GROUP BY / hierarchy
}
Build one by hand, deserialize it straight from a client's JSON body, or use the fluent builder:
var query = QueryBuilder.New()
.Select("Id", "Name", "Country")
.Sort(new SortDescriptor("Name"))
.Page(size: 20, number: 1)
.Build();
Filtering
Criteria is a tree: groups of conditions, joined by a Logic, where each group is itself joined to the others by a Logic.
var criteria = new QueryCriteria(
logic: Logic.And,
groups:
[
new ConditionGroup(
logic: Logic.Or,
conditions:
[
new Condition("Country", ConditionOperator.Equals, "USA"),
new Condition("Country", ConditionOperator.Equals, "Germany")
])
]);
var query = QueryBuilder.Where(criteria).Build();
Equivalent JSON from a client:
{
"criteria": {
"logic": 0, // Logic.And -> combine the groups below with AND (only one group here)
"groups": [
{
"logic": 1, // Logic.Or -> Country = 'USA' OR Country = 'Germany'
"conditions": [
{ "columnName": "Country", "operator": 0, "value": "USA" }, // operator 0 = Equals
{ "columnName": "Country", "operator": 0, "value": "Germany" } // operator 0 = Equals
]
}
]
}
}
Logic:0=And,1=Or,2=AndNot,3=OrNot
ConditionOperator covers the usual set: Equals, NotEquals, Contains, NotContains, StartsWith, EndsWith, LessThan, GreaterThan, LessThanOrEqualTo, GreaterThanOrEqualTo, Between.
Grouping
Add GroupByColumns and a provider turns a flat result into a nested tree instead — this library just defines that shape (HierarchyNode<T>: Key, Count, SubGroups, Items), a provider does the actual grouping:
var query = QueryBuilder.New()
.GroupBy(new GroupByDescriptor("Country"), new GroupByDescriptor("Department"))
.Page(5, 1) // paginates the top-level groups
.Build();
Validation
Never trust a Query that came from outside your API. Validate() supports two modes:
| Mode | Behavior |
|---|---|
SilentStrip |
Quietly removes denied/disallowed columns and clamps paging — the request still succeeds. |
ThrowException |
Throws a QueryValidationException listing every violated rule. |
query.Validate(rules =>
{
rules.Select(c => c.Deny("PasswordHash", "SecretKey"));
rules.PageSize(p => p.Max(100));
}, QueryValidationMode.SilentStrip);
The result contract
Every provider returns the same QueryResult<TModel>, whichever mode you used:
public record QueryResult<TModel>
{
public QueryResultMeta Meta { get; init; } // { Total: { Rows, Pages }, Type }
public IReadOnlyList<TModel> Models { get; init; } // populated when Type == Flat
public IReadOnlyList<HierarchyNode<TModel>> Groups { get; init; } // populated when Type == Grouped
}
Providers built on this core
| Provider | Package | Status |
|---|---|---|
| Dapper (SQL Server) | PepperX.QueryForge.Dapper |
✅ Released |
| Entity Framework Core | PepperX.QueryForge.EFCore |
🔧 In Development |
| In-Memory | PepperX.QueryForge.InMemory |
📋 Planned |
🤝 Contributing & License
This project is part of the PepperX Ecosystem. Licensed under the MIT License — see the LICENSE file for details.
| Product | Versions 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. |
-
net10.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on PepperX.QueryForge:
| Package | Downloads |
|---|---|
|
PepperX.QueryForge.Dapper
High-performance execution provider for the PepperX.QueryForge core library. It compiles abstract query models into parameterized SQL and executes them with Dapper against SQL Server, PostgreSQL, MySQL/MariaDB and Oracle, handling hierarchical grouping, complex filtering, and function/procedure integration. No schema permissions or deployed database objects required. |
|
|
PepperX.QueryForge.InMemory
In-memory execution provider for PepperX.QueryForge. Runs the same dynamic filtering, sorting, paging and hierarchical grouping against any IEnumerable or IQueryable — no database, no dependencies. Ideal for cached collections, API composition, unit tests, and as the reference implementation the database providers are asserted against. |
|
|
PepperX.QueryForge.EFCore
Entity Framework Core execution provider for PepperX.QueryForge. Translates abstract query models into expression trees so EF Core generates the SQL, which means dynamic filtering, sorting, paging and hierarchical grouping work on every database EF Core supports and respect your entity model, global query filters and value converters. |
GitHub repositories
This package is not used by any popular GitHub repositories.