Filter
A Filter is the most common top-level shape. It combines filtering, projection, ordering, and pagination in a single object, and is the input to ToListAsync<T>(Filter) and its dynamic / sync siblings.
Properties
| Property | Type | Description |
|---|---|---|
ConditionGroup | ConditionGroup? | Optional where-clause. |
Selects | List<string>? | Optional field projection (like SQL SELECT col1, col2). |
Orders | List<OrderBy>? | Optional sort criteria. |
Page | PageBy? | Optional pagination. |
where → order → page → select. Ordering and paging run on the full entity, then projection is applied to the trimmed page — so you can sort by a field you don't include in Selects.C# example
var filter = new Filter
{
ConditionGroup = new ConditionGroup
{
Connector = Connector.And,
Conditions = new List<Condition>
{
new Condition
{
Sort = 1,
Field = "IsActive",
DataType = DataType.Boolean,
Operator = Operator.Equal,
Values = new List<object> { true }
}
}
},
Selects = new List<string> { "Id", "Name", "Email" },
Orders = new List<OrderBy>
{
new OrderBy { Sort = 1, Field = "CreatedAt", Direction = Direction.Descending }
},
Page = new PageBy { PageNumber = 1, PageSize = 25 }
};
FilterResult<Customer> result = await dbContext.Customers.ToListAsync(filter);JSON example
{
"conditionGroup": {
"connector": "And",
"conditions": [
{ "sort": 1, "field": "IsActive", "dataType": "Boolean", "operator": "Equal", "values": [true] }
],
"subConditionGroups": []
},
"selects": ["Id", "Name", "Email"],
"orders": [
{ "sort": 1, "field": "CreatedAt", "direction": "Descending" }
],
"page": { "pageNumber": 1, "pageSize": 25 }
}Clone
Filter.Clone() is public since 3.3.0. It returns a deep copy — the condition tree with its groups and conditions, the Selects list, each order and the page — so nothing either request is given afterwards reaches the other.
Every node is new. The values a condition carries stay the caller's own objects, in a new list: they are scalars decoded from JSON and nothing in the pipeline writes to them.
Filter page2 = caller.Clone();
page2.Page!.PageNumber = 2; // the caller's own filter is untouchedReading one request again with a part changed, the next page or another order, used to mean rebuilding it around the caller's own clauses, which leaves both requests holding one condition tree: a rewrite of either reaches both. The library has cloned before rewriting anything since 3.0; callers could not until now. A branch the caller left null stays null, and a null entry inside a list is copied as a null entry rather than failing on it (3.3.0), so the refusal belongs to the method that runs the request and reads the same for a copy.
See also
- FilterResult<T> → the return shape.
- ToListAsync<T>(Filter) →
- ToListAsyncDynamic<T>(Filter) → for projection to
dynamic. - Filter example →