DynamicWhere.ex
DynamicWhere.exv2.1.0·docs

.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
ParameterTypeDescription
summarySummaryComposition object — ConditionGroup, GroupBy (required), Having, Orders, Page

Pipeline

  • Where — optional pre-grouping filter via ConditionGroup.
  • Grouprequired; groups by GroupBy.Fields and computes GroupBy.AggregateBy aggregations.
  • Having — optional post-group filter. Each condition's Field must reference an AggregateBy.Alias.
  • Order — optional sort. Fields must be GroupBy fields or aggregate aliases.
  • Page — optional pagination.

Validations

  • GroupBy is 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 PageNumber and PageSize must 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