FlexQuery.NET.Parsers.MiniOData
3.1.0
See the version list below for details.
dotnet add package FlexQuery.NET.Parsers.MiniOData --version 3.1.0
NuGet\Install-Package FlexQuery.NET.Parsers.MiniOData -Version 3.1.0
<PackageReference Include="FlexQuery.NET.Parsers.MiniOData" Version="3.1.0" />
<PackageVersion Include="FlexQuery.NET.Parsers.MiniOData" Version="3.1.0" />
<PackageReference Include="FlexQuery.NET.Parsers.MiniOData" />
paket add FlexQuery.NET.Parsers.MiniOData --version 3.1.0
#r "nuget: FlexQuery.NET.Parsers.MiniOData, 3.1.0"
#:package FlexQuery.NET.Parsers.MiniOData@3.1.0
#addin nuget:?package=FlexQuery.NET.Parsers.MiniOData&version=3.1.0
#tool nuget:?package=FlexQuery.NET.Parsers.MiniOData&version=3.1.0
FlexQuery.NET.Parsers.MiniOData
Lightweight OData-compatible query syntax parser for FlexQuery.NET.
When to Use This Package
Install this package if you are migrating from OData or need to support existing OData clients. The parser allows OData and native FlexQuery query syntax to coexist on the same endpoint, enabling gradual migration without changing controller code.
Installation
dotnet add package FlexQuery.NET.Parsers.MiniOData
Registration
builder.Services.AddFlexQueryMiniOData();
Quick Start
GET /api/products?$filter=Price gt 50 and Status eq 'Active'&$orderby=Name desc&$top=20&$skip=0
[HttpGet("products")]
public async Task<IActionResult> GetProducts([FromQuery] FlexQueryParameters parameters)
{
var result = await _context.Products.FlexQueryAsync(parameters, opts =>
{
opts.AllowedFields = new HashSet<string> { "Id", "Name", "Price", "Status" };
});
return Ok(result);
}
Supported OData Parameters
| Parameter | Maps To |
|---|---|
$filter |
QueryOptions.Filter |
$orderby |
QueryOptions.Sort |
$select |
QueryOptions.Select |
$top |
Paging.PageSize |
$skip |
Calculated from Paging.Page |
$expand |
QueryOptions.Includes |
Features
- Auto-Detection —
MiniODataQueryParserdetects OData parameters and parses them automatically - No Migration Required — Both OData and native FlexQuery syntax work on the same endpoints
- Seamless Integration — Register once to enable automatic OData parameter parsing
- Standalone Parsing —
ODataQueryParameterParser.Parse()for direct OData-to-QueryOptionsconversion
Known Limitations
- The parser implements a subset of the full OData specification — not all OData functions and expressions are supported
$expandsupport is limited to simple navigation property includes$countaggregation semantics differ from OData's$inlinecount
Related Packages
- FlexQuery.NET — Core query engine
- FlexQuery.NET.Parsers.Jql — Alternative parser for JQL syntax
Documentation
| 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 is compatible. 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 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
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
-
net6.0
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
-
net8.0
- FlexQuery.NET (>= 3.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
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 |
|---|---|---|
| 4.0.0-preview.3 | 31 | 7/18/2026 |
| 4.0.0-preview.2 | 37 | 7/14/2026 |
| 4.0.0-preview.1 | 46 | 7/12/2026 |
| 3.1.1 | 108 | 7/4/2026 |
| 3.1.0 | 114 | 6/26/2026 |
| 3.0.6 | 110 | 6/24/2026 |
| 3.0.5 | 112 | 6/24/2026 |
| 3.0.4 | 102 | 6/23/2026 |
| 3.0.3 | 100 | 6/23/2026 |
| 3.0.2 | 111 | 6/22/2026 |
| 3.0.1 | 106 | 6/22/2026 |
# FlexQuery.NET v3.1.0 Release Notes
**Release date:** 2026-06-25
## Overview
v3.1.0 introduces query execution observability, AG Grid camelCase support, HAVING query pipeline fixes, a shared projection metadata layer unifying Dapper and EF Core serialization behavior, DynamicType materialization for GroupBy/aggregate results, and a new diagnostics API. The Dapper return type is unified with EF Core (`QueryResult<object>`).
---
## What's New
### 1. Execution Diagnostics / Observability
New `IFlexQueryExecutionListener` interface with 4 lifecycle events that let you observe the full query pipeline:
| Event | When | Data |
|-------|------|------|
| `QueryParsed` | After parsing input parameters | `QueryOptions`, elapsed time |
| `QueryTranslated` | After SQL generation | SQL string, parameters |
| `QueryExecuted` | After database query returns rows | Row count, optional exception |
| `QueryMaterialized` | After `QueryResult<object>` is built | Final result, optional exception |
Both Dapper and EF Core providers now accept an optional `configureExecution` callback on all overloads:
```csharp
var result = await connection.FlexQueryAsync<Customer>(parameters, opts,
exec => exec.Listener = new MyTelemetryListener());
```
New types:
- `FlexQueryExecutionConfig` — config class with `Listener` property
- `FlexQueryExecutionContext` — internal context carrying `QueryId`, `Stopwatch`, listener, and `CancellationToken`
- `FlexQueryExecutionEvent` base class with event data types `QueryParsedEvent`, `QueryTranslatedEvent`, `QueryExecutedEvent`, `QueryMaterializedEvent`
- `IFlexQueryExecutionListener` — 4 async methods for lifecycle hooks
### 2. AG Grid SSRM camelCase Support
`AgGridResponseConverter.Convert()` and `ToAgGridServerSideResponse()` extension methods accept a new `camelCase` parameter. When `true`, POCO property names in row data dictionaries are automatically converted from PascalCase to camelCase. Group metadata field names remain configurable via `AgGridResponseFieldOptions`.
### 3. DynamicType for GroupBy / Aggregate Results
GroupBy and aggregate queries now produce `DynamicType` instances instead of `Dictionary<string, object>`. DynamicType properties flow through ASP.NET Core's `PropertyNamingPolicy`, so camelCase serialization works automatically without manual conversion.
### 4. Shared Projection Metadata Layer
`ProjectionMetadataBuilder` extracted as a shared component used by both Dapper and EF Core providers. Reduces duplication and ensures consistent projection resolution (nested paths, field types, IEnumerable detection) across both pipeline implementations.
### 5. Sample Web API Project
New `FlexQuery.NET.Samples.WebApi` demonstrating EF Core, Dapper, AG Grid SSRM, and Kendo UI integrations with SQLite, Swagger, and demo frontends.
### 6. Benchmarks
- `DapperSqlGenerationBenchmarks` — simple, complex-filter, and aggregate SQL generation against SqlServer dialect
- `ProjectionBenchmarks` — `SelectTreeBuilder`, `DynamicTypeBuilder` cache hit/miss, `QueryCacheKeyBuilder` performance
---
## What's Fixed
### 1. HAVING Pipeline — Parser, SQL Generation, Alias Naming
Three independent bugs that caused HAVING clauses to be silently dropped or return incorrect results:
**a) HavingParser regex regression:**
The field portion of the HAVING expression was non-optional. Input like `count:gt:20` would misinterpret `gt` as a field name and return `null` because no field was found before the colon.
```csharp
// Before: field-less aggregate HAVING parsed as null
var having = parser.Parse("count:gt:20"); // null
// After:
var having = parser.Parse("count:gt:20"); // { Field: null, Aggregate: Count, Operator: GreaterThan, Value: 20 }
```
**b) Dapper SqlTranslator.BuildHavingClause:**
Field-less aggregates (`COUNT(*)`) emitted quoted `[*]` columns (`COUNT([*])`) and bound comparison values as strings (`'20'` instead of `20`). SQLite rejected `INTEGER > TEXT` comparisons, returning zero rows.
```sql
-- Before (broken for SQLite):
HAVING COUNT([*]) > '20'
-- After:
HAVING COUNT(*) > @p0 -- @p0 = 20 (int)
```
**c) BuildAggregateAlias:**
Field-less aggregates produced `allCount` as the alias instead of the predictable `Count`.
```sql
-- Before:
SELECT COUNT(*) AS [allCount]
-- After:
SELECT COUNT(*) AS [Count]
```
### 2. Dapper TotalCount / ResultCount Alignment with EF Core
**Before:** `ResultCount` was unconditionally set to `items.Count` (the page size), even when `IncludeTotalCount` was false.
**After:** `ResultCount` is only set when counts are explicitly enabled. Checks both `options.IncludeCount` (user-level) and `execOptions.IncludeTotalCount` (server-level). Both providers now return identical `QueryResult` structure for all scenarios.
### 3. Dapper Connection Lifecycle
Documented that `FlexQueryAsync<T>()` auto-opens the connection if closed but never auto-closes it. Added `CancellationToken` parameter to `ExecuteQueryAsync`.
---
## Breaking Changes
### 1. Dapper Return Type: `QueryResult<T>` → `QueryResult<object>`
**Affected package:** `FlexQuery.NET.Dapper`
**Before:** `FlexQueryAsync<T>()` returned `Task<QueryResult<T>>`.
**After:** Returns `Task<QueryResult<object>>`.
This unifies the Dapper provider's return type with the EF Core provider. Required because Dapper now uses `DynamicType` for projection materialization, and the projected type is not known at compile time.
**Migration:**
```csharp
// Before (will not compile):
QueryResult<Customer> result = await connection.FlexQueryAsync<Customer>(...);
// After:
QueryResult<object> result = await connection.FlexQueryAsync<Customer>(...);
// OR:
var result = await connection.FlexQueryAsync<Customer>(...);
```
### 2. DebugResult Namespace Change
**Affected package:** `FlexQuery.NET`
`DebugResult` moved from inline class in `FlexQuery.NET.Extensions` (inside `FlexQueryDebugExtensions.cs`) to `FlexQuery.NET.Models`.
**Migration:** Add `using FlexQuery.NET.Models;` where `DebugResult` is referenced.
---
## Behavioral Changes
| Change | Impact | Mitigation |
|--------|--------|------------|
| Dapper return type → `QueryResult<object>` | Compile break on explicit return type annotations | Use `var` or change to `QueryResult<object>` |
| `DebugResult` → `FlexQuery.NET.Models` | Compile break if relying on `FlexQuery.NET.Extensions` | Add `using FlexQuery.NET.Models;` |
| Dapper `ResultCount` null when count is off | Matches EF Core; previously returned `items.Count` | Add null check if needed |
| Field-less HAVING `Count` alias | Previously `allCount` | Update any HAVING filter referencing `allCount` |
| GroupBy/aggregate rows → `DynamicType` | Property names respect JSON naming policy; no longer `Dictionary<string, object>` | Access via properties instead of dictionary keys, or use reflection/DynamicType API |
---
## New Test Coverage
### HAVING Pipeline (19 new tests)
| Test | Area | Verifies |
|------|------|----------|
| `Parse_Having_FieldLessAggregate` | Parser | `count:gt:10` parses with null field |
| `Parse_Having_ParenthesizedValue` | Parser | `(count:gt:10)` parenthesized syntax |
| `Parse_Having_ColonSeparatedField` | Parser | Field-based HAVING with colons |
| `Parse_Having_Count` | Parser | `count` aggregate parsed |
| `Parse_Having_Sum` | Parser | `sum` aggregate parsed |
| `Parse_Having_Avg` | Parser | `avg` aggregate parsed |
| `Parse_Having_Max` | Parser | `max` aggregate parsed |
| `Parse_Having_Min` | Parser | `min` aggregate parsed |
| `BuildHavingClause_CountStar_NoField` | SQL generation | `COUNT(*) > @p0` without field |
| `BuildHavingClause_SumField` | SQL generation | `SUM([Amount]) > @p0` with field |
| `BuildHavingClause_AvgField` | SQL generation | `AVG([Amount]) > @p0` with field |
| `BuildHavingClause_MaxField` | SQL generation | `MAX([Amount]) > @p0` with field |
| `BuildHavingClause_MinField` | SQL generation | `MIN([Amount]) > @p0` with field |
| `BuildAggregateAlias_FieldLessAggregate_ReturnsCount` | Alias | Field-less → `Count` |
| `BuildAggregateAlias_FieldAggregate_ReturnsFieldFunction` | Alias | Field + function → `AmountSum` |
| `Having_Count_Grouped_Sorted_Paged` | Integration | Full pipeline: Count HAVING + sort + paging |
| `Having_Sum_Grouped_Sorted_Paged` | Integration | Full pipeline: Sum HAVING + sort + paging |
| `Having_Avg_Grouped_Projected` | Integration | HAVING with projection |
| `Having_Max_Min_Grouped_Projected` | Integration | Multiple aggregates HAVING with projection |
### GroupBy / Integration (204 new test lines)
Additional GroupBy execution tests across EF Core and Dapper covering dict vs DynamicType materialization, grouping with sort/paging combinations, and aggregate result structures.
### Parser (117 new test lines)
Additional parser coverage for edge cases and boundary conditions.
### AgGridResponseConverter
Adapted existing tests to cover the new `camelCase` parameter (both `true` and `false` paths).
---
## Additional Changes (post-commit)
- **ToQueryOptions extension method:** New `FlexQueryParametersExtensions.ToQueryOptions()` convenience extension on `FlexQueryParameters` for direct-to-`QueryOptions` parsing without calling `QueryOptionsParser.Parse()`.
- **Sample controllers simplified:** `DapperCustomersController` and `EfCustomersController` migrated to primary constructors and simplified to use the full FlexQueryAsync API instead of manual GroupBy handling.
- **MiniOData assembly name fix:** `QueryOptionsParser` registration corrected from `FlexQuery.NET.MiniOData` to `FlexQuery.NET.Parsers.MiniOData`.
- **Internal call sites updated:** `FlexQueryDapperExtensions`, `ProjectionEfCoreExtensions`, `QueryableEfCoreExtensions`, and `QueryableExtensions` all updated to use `parameters.ToQueryOptions()`.
---
## Migration Guide
### Dapper Return Type
Replace explicit `QueryResult<T>` with `QueryResult<object>` or use `var`:
```csharp
// Before:
QueryResult<Customer> result = await connection.FlexQueryAsync<Customer>(...);
// After:
var result = await connection.FlexQueryAsync<Customer>(...);
```
### HAVING Alias
If you reference the auto-generated alias for field-less aggregate HAVING filters, replace `allCount` with `Count`:
```csharp
// Before:
options.Having.Add(new HavingNode("allCount:gt:10"));
// After:
options.Having.Add(new HavingNode("Count:gt:10"));
```
### DebugResult Import
```csharp
// Add this using:
using FlexQuery.NET.Models;
```
---
## Upgrading
```bash
dotnet add package FlexQuery.NET --version 3.1.0
dotnet add package FlexQuery.NET.Dapper --version 3.1.0
dotnet add package FlexQuery.NET.EntityFrameworkCore --version 3.1.0
dotnet add package FlexQuery.NET.Parsers.Jql --version 3.1.0
dotnet add package FlexQuery.NET.Parsers.MiniOData --version 3.1.0
dotnet add package FlexQuery.NET.Adapters.AgGrid --version 3.1.0
dotnet add package FlexQuery.NET.Adapters.Kendo --version 3.1.0
dotnet add package FlexQuery.NET.AspNetCore --version 3.1.0
```