A simple dynamic expression-tree query builder. You pass it a nested collection of filters for an object, and it materializes a query capable of acting as the filter to an arbitrary collection.
$ dotnet add package Castle.DynamicLinqQueryBuilderdynamic-linq-query-builder is a small library that allows any .NET collection to be filtered dynamically at runtime.
Version 2.0 is a performance-focused release that targets .NET 8+ exclusively. By dropping support for legacy frameworks (.NET 4.5, .NET Standard 2.0, .NET 6), we were able to leverage modern .NET APIs that simply weren't available before:
FrozenDictionary for faster operator dispatchSystem.DateOnly instead of a custom workaroundThe result is a faster, leaner library with new opt-in features for scenarios where performance really matters.
Still on an older framework? Version 1.3.4 remains available on NuGet and continues to support .NET 4.5, .NET Standard 2.0, .NET 6, and .NET 8.
IQueryable from any collection and filter combinationEF6, EF Core, and MongoDB Client >=2.19IFilterOperator interface and optionsStringComparison.OrdinalIgnoreCase mode for in-memory scenariosin operations| Scenario | v1.3.4 | v2.0 | Improvement |
|---|---|---|---|
Large in (500 values) | 3,525 μs | 421 μs | 88% faster |
| Repeated queries (cached) | 210 μs | 1.4 μs | 150x faster |
| String filter (ordinal mode) | 490 μs | 309 μs | 37% faster, 85% less memory |
All new features are opt-in — your existing code works without changes.
If you're running the same filter structure repeatedly (e.g., in a loop or API endpoint), enable caching to skip redundant expression building:
var options = new BuildExpressionOptions
{
EnableExpressionCaching = true, // Cache built expression trees
EnablePredicateCaching = true // Cache compiled delegates
};
// First call builds and caches; subsequent calls are ~150x faster
var results = myCollection.BuildQuery(filter, options).ToList();For in-memory scenarios (not EF Core or other ORMs), enable ordinal string comparison to avoid ToLower() allocations:
var options = new BuildExpressionOptions
{
UseOrdinalStringComparison = true // Uses StringComparison.OrdinalIgnoreCase
};
var results = myCollection.BuildQuery(filter, options).ToList();Note: Keep this
false(default) when using with EF Core, EF6, or other ORMs that translate expressions to SQL. TheToLower()approach is required for database compatibility.
Install via NuGet UI or Package Manager Console:
PM> Install-Package Castle.DynamicLinqQueryBuilder
For System.Text.Json support:
PM> Install-Package Castle.DynamicLinqQueryBuilder.SystemTextJson
Targets: .NET 8+
Migrating to v2.0 is straightforward:
net8.0 or laterIf you can't upgrade to .NET 8, stay on v1.3.4 — it's still fully functional and available on NuGet.
The easiest way to get started is to install the NuGet package and take a look at the MVC sample application included in the source code. It contains a working example of both dynamic-linq-query-builder and jQuery-QueryBuilder.
For more details, see the Wiki.
Contributions and pull requests are welcome with associated unit tests.