.Summary<T>(summary)
Applies the full aggregate-reporting pipeline — where → group → having → order → page — to a query and returns a dynamic IQueryable.
Signature
public static IQueryable Summary<T>(this IQueryable<T> query, Summary summary)
where T : class| Parameter | Type | Description |
|---|---|---|
summary | Summary | Composition object — ConditionGroup, GroupBy (required), Having, Orders, Page |
Pipeline
Where— optional pre-grouping filter viaConditionGroup.Group— required; groups byGroupBy.Fieldsand computesGroupBy.AggregateByaggregations.Having— optional post-group filter. Each condition'sFieldmust reference anAggregateBy.Alias.Order— optional sort. Fields must be GroupBy fields or aggregate aliases.Page— optional pagination.
Validations
GroupByis required (not null) —ArgumentNullException.- GroupBy validation — see
.Group<T>. - Order fields must exist in GroupBy fields or aggregate aliases —
SummaryOrderFieldMustExistInGroupByOrAggregate({field}). - Having condition fields must reference aggregate aliases —
HavingFieldMustExistInAggregateByAlias({field}). - Page validation — both
PageNumberandPageSizemust be > 0.
Warning
In a
Summary, the Having.ConditionGroup.Conditions[].Field must match an AggregateBy.Alias, not an entity property path.Note
Dotted GroupBy fields (e.g.,
Category.Name) produce flattened alias keys in the dynamic result objects (e.g., CategoryName). Order fields in Summary.Orders should use the dotted form; the library handles alias mapping internally.Returns
IQueryable — dynamic grouped query. Each element exposes the GroupBy fields (flattened to alias keys) plus all AggregateBy.Alias values.
Example
var summary = new Summary
{
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 } }
}
},
GroupBy = new GroupBy
{
Fields = new List<string> { "Category.Name" },
AggregateBy = new List<AggregateBy>
{
new AggregateBy { Field = null, Alias = "ProductCount", Aggregator = Aggregator.Count },
new AggregateBy { Field = "Price", Alias = "AvgPrice", Aggregator = Aggregator.Average },
new AggregateBy { Field = "Price", Alias = "TotalRevenue", Aggregator = Aggregator.Sumation }
}
},
Having = new ConditionGroup
{
Connector = Connector.And,
Conditions = new List<Condition>
{
new Condition { Sort = 1, Field = "ProductCount", DataType = DataType.Number, Operator = Operator.GreaterThan, Values = new List<object> { 5 } }
}
},
Orders = new List<OrderBy>
{
new OrderBy { Sort = 1, Field = "TotalRevenue", Direction = Direction.Descending }
},
Page = new PageBy { PageNumber = 1, PageSize = 10 }
};
IQueryable grouped = dbContext.Products.Summary(summary);{
"conditionGroup": {
"connector": "And",
"conditions": [
{ "sort": 1, "field": "IsActive", "dataType": "Boolean", "operator": "Equal", "values": ["true"] }
],
"subConditionGroups": []
},
"groupBy": {
"fields": ["Category.Name"],
"aggregateBy": [
{ "field": null, "alias": "ProductCount", "aggregator": "Count" },
{ "field": "Price", "alias": "AvgPrice", "aggregator": "Average" },
{ "field": "Price", "alias": "TotalRevenue", "aggregator": "Sumation" }
]
},
"having": {
"connector": "And",
"conditions": [
{ "sort": 1, "field": "ProductCount", "dataType": "Number", "operator": "GreaterThan", "values": ["5"] }
],
"subConditionGroups": []
},
"orders": [
{ "sort": 1, "field": "TotalRevenue", "direction": "Descending" }
],
"page": { "pageNumber": 1, "pageSize": 10 }
}See also
Summaryshape..ToList<T>(Summary)/.ToListAsync<T>(Summary)to materialize.- Summary validation.