DynamicWhere.ex
2.1.1
dotnet add package DynamicWhere.ex --version 2.1.1
NuGet\Install-Package DynamicWhere.ex -Version 2.1.1
<PackageReference Include="DynamicWhere.ex" Version="2.1.1" />
<PackageVersion Include="DynamicWhere.ex" Version="2.1.1" />
<PackageReference Include="DynamicWhere.ex" />
paket add DynamicWhere.ex --version 2.1.1
#r "nuget: DynamicWhere.ex, 2.1.1"
#:package DynamicWhere.ex@2.1.1
#addin nuget:?package=DynamicWhere.ex&version=2.1.1
#tool nuget:?package=DynamicWhere.ex&version=2.1.1
<div align="center">
DynamicWhere.ex
JSON-driven queries for Entity Framework Core.
</div>
A powerful, versatile library for dynamically composing complex filter, sort, paginate, group, aggregate, and set-operation (Union / Intersect / Except) expressions in Entity Framework Core applications — all driven by simple JSON objects from any front-end or API consumer.
Full reference, JSON cookbook, and tuning guide → doc.dynamicwhere.com
Why DynamicWhere.ex?
Stop concatenating LINQ predicates by hand. Your front-end sends one JSON shape; the back-end calls a single extension method. You get back a strongly-typed, paginated result.
- JSON in →
IQueryable<T>out. No string LINQ. No manual expression trees. - Three composable shapes —
Filter,Segment,Summary— cover where, set operations, and group-by reporting. - Seventeen extension methods on
IQueryable<T>andIEnumerable<T>. - Nested navigation through references and collections, with auto-wrapped
.Any()lambdas where needed. - Heterogeneous
Condition.Values— pass raw numbers, booleans, strings; normalized perDataType. - Thread-safe reflection cache with FIFO / LRU / LFU eviction and six tuned presets.
- Free Forever. Targets .NET 6, 7, 8, 9.
Install
dotnet add package DynamicWhere.ex --version 2.1.0
Or via Package Manager:
Install-Package DynamicWhere.ex -Version 2.1.0
Dependencies (restored automatically):
| Package | Version |
|---|---|
Microsoft.EntityFrameworkCore |
6.0.22 |
System.Linq.Dynamic.Core |
1.6.7 |
Quick Start
Front-end / API body — pure JSON:
{
"conditionGroup": {
"connector": "And",
"conditions": [
{ "sort": 1, "field": "Price", "dataType": "Number", "operator": "GreaterThan", "values": [50] },
{ "sort": 2, "field": "Category.Name", "dataType": "Text", "operator": "IEqual", "values": ["electronics"] }
],
"subConditionGroups": []
},
"selects": ["Id", "Name", "Price", "Category.Name"],
"orders": [{ "sort": 1, "field": "Price", "direction": "Descending" }],
"page": { "pageNumber": 1, "pageSize": 10 }
}
Back-end — one method call:
using DynamicWhere.ex.Source;
using DynamicWhere.ex.Classes.Complex;
app.MapPost("/products/search", async (Filter filter, AppDbContext db) =>
{
FilterResult<Product> result = await db.Products.ToListAsync(filter);
return Results.Ok(result);
});
Response shape (FilterResult<Product>):
{
"pageNumber": 1,
"pageSize": 10,
"pageCount": 5,
"totalCount": 42,
"data": [
{ "id": 7, "name": "Laptop Pro", "price": 1299.99, "category": { "name": "Electronics" } }
],
"queryString": null
}
That's the whole loop. Full walk-through in Quick Start.
What's inside
Three composable shapes
| Shape | Pipeline | Use when |
|---|---|---|
Filter |
where → order → page → select | Standard list / search / detail endpoints |
Segment |
set1 ∪/∩/∖ set2 ∪/∩/∖ set3 → order → page | UNION / INTERSECT / EXCEPT across multiple condition sets |
Summary |
where → group → having → order → page | Aggregate reporting (GROUP BY + SUM / AVG / COUNT …) |
Seventeen extension methods
Projection, filtering, composition, and materialization on IQueryable<T> and IEnumerable<T>:
| Group | Methods |
|---|---|
| Projection | .Select<T>(fields) · .SelectDynamic<T>(fields) |
| Filtering | .Where<T>(Condition) · .Where<T>(ConditionGroup) |
| Composition | .Order<T> · .Page<T> · .Group<T> · .Filter<T> · .FilterDynamic<T> · .Summary<T> |
| Materialization | .ToList<T>(Filter) · .ToListAsync<T>(Filter) · .ToListDynamic<T>(Filter) · .ToListAsyncDynamic<T>(Filter) · .ToList<T>(Summary) · .ToListAsync<T>(Summary) · .ToListAsync<T>(Segment) |
Full signatures, validations, and return types → Extension Methods Reference.
Operators & data types
Twenty-eight comparison operators across seven data types — case-sensitive and case-insensitive variants of every text operation:
- Equality:
Equal·IEqual·NotEqual·INotEqual - Substring:
Contains·IContains·NotContains·INotContains·StartsWith·IStartsWith·EndsWith·IEndsWith(+Not*andI*of each) - Set:
In·IIn·NotIn·INotIn - Range:
GreaterThan·GreaterThanOrEqual·LessThan·LessThanOrEqual·Between·NotBetween - Null:
IsNull·IsNotNull
Data types: Text · Guid · Number · Boolean · DateTime · Date · Enum. Full matrix → DataType reference.
Aggregations
Count · CountDistinct · Sumation · Average · Minimum · Maximum · FirstOrDefault · LastOrDefault. With optional Having post-filter referencing aggregate aliases.
Nested navigation
Dotted paths through reference and collection properties, with .Any() lambdas inserted automatically where the path crosses a collection.
{ "field": "Orders.OrderItems.ProductName", "dataType": "Text",
"operator": "IContains", "values": ["laptop"] }
Becomes:
Orders.Any(i1 => i1.OrderItems.Any(i2 =>
i2.ProductName != null && i2.ProductName.ToLower().Contains("laptop")))
Reflection cache
A thread-safe ConcurrentDictionary-backed cache across three stores (TypeProperties · PropertyPath · CollectionElementType) eliminates reflection overhead on repeated queries. Three eviction strategies and six tuned presets:
| Preset | MaxSize | Eviction | Use case |
|---|---|---|---|
Default |
1000 | LRU | General purpose |
ForHighMemoryEnvironment() |
5000 | LRU | Servers with ample RAM |
ForLowMemoryEnvironment() |
250 | LFU | Constrained environments |
ForDevelopment() |
100 | FIFO | Testing & debugging |
ForHighFrequencyAccess() |
2000 | LFU | Repeated queries on same types |
ForTemporalAccess() |
1500 | LRU | Recent-access-heavy workloads |
using DynamicWhere.ex.Optimization.Cache.Source;
CacheExpose.Configure(CacheOptions.ForHighMemoryEnvironment());
CacheExpose.WarmupCache<Product>("Name", "Category.Name", "Price");
Full tuning guide → Cache & Optimization.
Error handling
Every validation failure throws LogicException with a structured error code. Catch at your API boundary and surface as a 400:
try
{
var result = await db.Products.ToListAsync(filter);
return Results.Ok(result);
}
catch (LogicException ex)
{
return Results.BadRequest(new { code = ex.Message });
}
Full code reference → Error Codes.
Documentation
The complete reference — every enum, class, extension method, validation rule, JSON example, and cache option — lives on the official site:
→ doc.dynamicwhere.com
| Section | What's there |
|---|---|
| Getting Started | Introduction, installation, quick start |
| Enums | Every DataType, Operator, Connector, Direction, Intersection, Aggregator, Cache enum |
| Classes | Condition, ConditionGroup, ConditionSet, OrderBy, GroupBy, AggregateBy, PageBy, Filter, Segment, Summary, Result types |
| Extension Methods | All 17 methods with signatures, validations, examples |
| Validation Rules | What's checked and what throws |
| JSON Cookbook | 13 copy-pasteable end-to-end examples |
| Cache & Optimization | Architecture, stores, options, presets, monitoring |
| Error Codes | Every LogicException message |
| Breaking Changes | Known limits and migration notes |
Version 2.1.0 highlights
- Heterogeneous
Condition.Values—List<object>with type-safe coercion. Send raw numbers and booleans without quoting. Backward-compatible withList<string>callers. - Six tuned cache presets — pick
ForHighMemory,ForLowMemory,ForDevelopment,ForHighFrequencyAccess,ForTemporalAccess, or the default. - Official documentation site launched at
doc.dynamicwhere.com.
See Breaking Changes & Known Limitations for the complete migration / caveat list.
Compatibility
- .NET: 6, 7, 8, 9
- EF Core providers: SQL Server, PostgreSQL (Npgsql), MySQL (Pomelo), SQLite — anything that supports
ToQueryString()for the optionalgetQueryString: trueflag. - Enum storage: assumed stored as strings. Use
DataType.Numberif your column stores integers. - Case-insensitive operators: emit
.ToLower()on both sides. Works well on SQL Server's default collation; watch for case-sensitive PostgreSQLClocale.
Links
- Documentation: doc.dynamicwhere.com
- NuGet: nuget.org/packages/DynamicWhere.ex
- Source: github.com/Sajadh92/DynamicWhere.ex
- Issues: github.com/Sajadh92/DynamicWhere.ex/issues
License
Free Forever — Copyright © Sajjad H. Al-Khafaji.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net6.0
- Microsoft.EntityFrameworkCore (>= 6.0.22)
- System.Linq.Dynamic.Core (>= 1.6.7)
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.1.1 | 96 | 5/14/2026 | |
| 2.1.0 | 96 | 5/14/2026 | |
| 2.0.0 | 140 | 4/14/2026 | |
| 2.0.0-beta.4 | 449 | 3/6/2026 | |
| 2.0.0-beta.3 | 108 | 2/26/2026 | |
| 2.0.0-beta.2 | 90 | 2/23/2026 | |
| 2.0.0-beta.1 | 97 | 2/1/2026 | |
| 1.8.4 | 573 | 10/12/2024 | |
| 1.8.3 | 249 | 8/8/2024 | |
| 1.8.2 | 259 | 7/10/2024 | |
| 1.8.1 | 1,107 | 3/29/2024 | |
| 1.8.0 | 446 | 2/11/2024 | |
| 1.7.2 | 293 | 2/9/2024 | |
| 1.7.1 | 287 | 2/8/2024 | |
| 1.7.0 | 432 | 11/5/2023 | |
| 1.6.0 | 324 | 9/30/2023 | |
| 1.5.3 | 332 | 9/25/2023 | |
| 1.5.2 | 327 | 9/24/2023 | |
| 1.5.1 | 278 | 9/23/2023 | |
| 1.5.0 | 287 | 9/22/2023 |
v2.1.0 — Heterogeneous Condition.Values (List<object> with type coercion), six tuned cache presets, full docs site at https://doc.dynamicwhere.com/.