Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
Query Model
Every type described here lives in the PepperX.QueryForge namespace in the core package. They
are plain, serializable, provider-free. This page is the structural reference — what the fields are.
Query Semantics is the behavioural reference — what they mean when evaluated.
The root object. Deliberately a mutable class, not a record, because SilentStrip validation
rewrites it in place.
publicclassQuery{publicQueryCriteriaCriteria{get;set;}=new();publicQueryPagingPaging{get;set;}=new();publicIReadOnlyList<string>SelectColumns{get;set;}=Array.Empty<string>();publicIReadOnlyList<SortDescriptor>SortColumns{get;set;}=Array.Empty<SortDescriptor>();publicIReadOnlyList<GroupByDescriptor>GroupByColumns{get;set;}=Array.Empty<GroupByDescriptor>();}| Property | Meaning | Default |
|---|---|---|
Criteria | The filter tree. | empty — matches everything |
Paging | Which window of the result to return. | Size = 12, Number = 1 |
SelectColumns | Which columns to include. Empty means all. | empty |
SortColumns | Ordering, applied in list order. | empty |
GroupByColumns | Grouping levels, outermost first. Non-empty switches the result to a hierarchy. | empty |
Every collection is initialized to empty and never null. A provider can enumerate any of them without a null check, and a client can omit any of them from a JSON body.
A Query has no notion of a table, entity, or collection. The target is supplied by the
provider — DapperQuery.Object for Dapper, the IQueryable<T> for EF Core, the IEnumerable<T> for
In-Memory. This is a security property, not an oversight: a client posting a Query cannot choose
what it runs against. See Security.
A record holding groups of conditions and the operator that joins those groups.
publicrecordQueryCriteria{publicIReadOnlyList<ConditionGroup>Groups{get;init;}publicLogicLogic{get;init;}publicQueryCriteria(IReadOnlyList<ConditionGroup>?groups=null,Logiclogic=Logic.And);}| Property | Meaning |
|---|---|
Groups | The condition groups. Guaranteed non-null; the constructor substitutes an empty array. |
Logic | How the groups are joined to each other. Only the AND/OR part is used here — a NOT suffix at this level is ignored. |
An empty Groups means no filter at all, which matches everything.
publicrecordConditionGroup{publicIReadOnlyList<Condition>Conditions{get;init;}publicLogicLogic{get;init;}publicConditionGroup(IReadOnlyList<Condition>?conditions=null,Logiclogic=Logic.And);}| Property | Meaning |
|---|---|
Conditions | The conditions in this group. Guaranteed non-null. |
Logic | How the conditions inside this group are joined, and whether the whole group is negated. AndNot and OrNot negate. |
A group whose conditions are all unusable contributes nothing and is skipped entirely — it does not become an always-false clause. See Query Semantics.
publicrecordCondition(stringColumnName,ConditionOperatorOperator,object?Value=null,object?ValueTo=null);| Property | Meaning |
|---|---|
ColumnName | The column or property to filter on. Matched case-insensitively. |
Operator | Which comparison to apply. |
Value | The value to compare against. Typed as object? so a JSON body can carry anything. |
ValueTo | The upper bound. Used only by Between, ignored by every other operator. |
Value and ValueTo may arrive as System.Text.Json.JsonElement when the query was model-bound
from an HTTP request. Providers never have to think about that — ConditionSemantics.Unwrap converts
it before use. See JSON Contract.
publicrecordQueryPaging(intSize=12,intNumber=1);| Property | Meaning |
|---|---|
Size | Rows per page — or, for a grouped query, outermost groups per page. |
Number | 1-based page number. |
Both are normalized identically by every provider before use:
size=Size>0?Size:12;// non-positive falls back to the defaultnumber=Number>0?Number:1;offset=(number-1)*size;A page past the end is not an error — it returns an empty page with the true totals still reported, which is what a data grid needs in order to correct itself.
publicrecordSortDescriptor(stringColumnName,SortOrderSortOrder=SortOrder.Ascending):IColumnDescriptor;publicrecordGroupByDescriptor(stringColumnName,SortOrderSortOrder=SortOrder.Ascending):IColumnDescriptor;Structurally identical, semantically different:
SortColumnsorder the rows, applied in list order: the first is the primary key, the second breaks ties, and so on.GroupByColumnsdefine nesting levels, outermost first. Each level'sSortOrderorders that level's keys.Pagingapplies to the first level only.
Both implement IColumnDescriptor, which is what lets the validation engine apply column rules
generically:
publicinterfaceIColumnDescriptor{stringColumnName{get;}}publicenumLogic{And,Or,AndNot,OrNot}| Member | JSON value | Joins with | Negates |
|---|---|---|---|
And | 0 | AND | no |
Or | 1 | OR | no |
AndNot | 2 | AND | yes |
OrNot | 3 | OR | yes |
Negation is meaningful only on a ConditionGroup. At the QueryCriteria level the Not suffix
is ignored and only the AND/OR part is used.
publicenumSortOrder{Ascending,Descending}| Member | JSON value |
|---|---|
Ascending | 0 |
Descending | 1 |
publicenumConditionOperator{Equals,NotEquals,Contains,NotContains,StartsWith,EndsWith,LessThan,GreaterThan,LessThanOrEqualTo,GreaterThanOrEqualTo,Between}| Member | JSON value | Needs Value | Needs ValueTo | Notes |
|---|---|---|---|---|
Equals | 0 | no | no | a null Value means IS NULL |
NotEquals | 1 | no | no | a null Value means IS NOT NULL |
Contains | 2 | yes | no | text; wildcards in the value are escaped |
NotContains | 3 | yes | no | text |
StartsWith | 4 | yes | no | text |
EndsWith | 5 | yes | no | text |
LessThan | 6 | yes | no | |
GreaterThan | 7 | yes | no | |
LessThanOrEqualTo | 8 | yes | no | |
GreaterThanOrEqualTo | 9 | yes | no | |
Between | 10 | yes | yes | inclusive at both ends |
The JSON values are the C# enum's declaration order. They are part of the wire contract — do not reorder them. Full semantics for each are in Query Semantics.
publicenumQueryResultType{Flat,Grouped}Reported on QueryResult<T>.Meta.Type, and tells you which of Models / Groups is populated.
publicenumQueryValidationMode{SilentStrip,ThrowException}See Validation.
Described in full under Results and Metadata; summarized here for completeness.
publicrecordQueryResult<TModel>{publicQueryResultMetaMeta{get;init;}publicIReadOnlyList<TModel>Models{get;init;}// when Type == FlatpublicIReadOnlyList<HierarchyNode<TModel>>Groups{get;init;}// when Type == Grouped}publicrecordQueryResultMeta(QueryResultMetaTotalTotal,QueryResultTypeType);publicrecordQueryResultMetaTotal(intRows,intPages);publicrecordHierarchyNode<TModel>(object?Key,intCount,IReadOnlyList<HierarchyNode<TModel>>?SubGroups,IReadOnlyList<TModel>?Items);The Dapper provider adds one thing — the target object — by subclassing Query.
namespacePepperX.QueryForge.Dapper;publicclassDapperQuery:PepperX.QueryForge.Query{publicDapperQueryObject?Object{get;set;}}publicrecordDapperQueryObject(stringName,stringSchema="",DapperObjectTypeType=DapperObjectType.Auto,IReadOnlyDictionary<string,object?>?Parameters=null);publicenumDapperObjectType{Auto=0,Table=1,View=2,TVF=3,SP=4}| Field | Meaning |
|---|---|
Name | Table, view, function or procedure name. Required. |
Schema | Empty means "use the dialect's default": dbo on SQL Server, public on PostgreSQL, none on MySQL, Oracle or SQLite. |
Type | Auto, Table and View are all handled as "select from it". TVF and SP are invoked differently and must be declared. |
Parameters | Arguments for a function or procedure. TVF arguments are positional, passed in the order of this dictionary. Procedure arguments are named where the engine supports it. |
DapperQuery is deliberately a separate type from Query. Bind the client's body as a Query,
then upgrade it server-side with DapperQueryBuilder.FromBase(clientQuery) — the target is something
only your code can set. See Fluent Builders and Security.
QueryForge · part of the PepperX Ecosystem · MIT licensed · packages 2.0.0, .NET 10