/// <summary> /// Computes a time-sensitive count aggregate of the non-null values using snapshot semantics. /// </summary> public IAggregate <TSource, ulong, ulong> CountNotNull <TValue>(Expression <Func <TSource, TValue> > selector) { Invariant.IsNotNull(selector, nameof(selector)); var aggregate = new CountAggregate <TValue>(); return(aggregate.SkipNulls().Wrap(selector).ApplyFilter(this.Filter)); }
/// <summary> /// Computes a time-sensitive count aggregate using snapshot semantics. /// </summary> public IAggregate <TSource, ulong, ulong> Count() { var aggregate = new CountAggregate <TSource>(); return(aggregate.ApplyFilter(this.Filter)); }
internal static List <Entity> ProcessAggregateFetchXml(XrmFakedContext ctx, XDocument xmlDoc, List <Entity> resultOfQuery) { // Validate that <all-attributes> is not present, // that all attributes have groupby or aggregate, and an alias, // and that there is exactly 1 groupby. if (RetrieveFetchXmlNode(xmlDoc, "all-attributes") != null) { throw new Exception("Can't have <all-attributes /> present when using aggregate"); } var ns = xmlDoc.Root.Name.Namespace; var entityName = RetrieveFetchXmlNode(xmlDoc, "entity")?.GetAttribute("name")?.Value; if (string.IsNullOrEmpty(entityName)) { throw new Exception("Can't find entity name for aggregate query"); } var aggregates = new List <FetchAggregate>(); var groups = new List <FetchGrouping>(); foreach (var attr in xmlDoc.Descendants(ns + "attribute")) { //TODO: Find entity alias. Handle aliasedvalue in the query result. var alias = attr.GetAttribute("alias")?.Value; var logicalName = attr.GetAttribute("name")?.Value; if (string.IsNullOrEmpty("alias")) { throw new Exception("Missing alias for attribute in aggregate fetch xml"); } if (string.IsNullOrEmpty("name")) { throw new Exception("Missing name for attribute in aggregate fetch xml"); } if (attr.IsAttributeTrue("groupby")) { var dategrouping = attr.GetAttribute("dategrouping")?.Value; if (dategrouping != null) { DateGroupType t; if (!Enum.TryParse(dategrouping, true, out t)) { throw new Exception("Unknown dategrouping value '" + dategrouping + "'"); } groups.Add(new DateTimeGroup() { Type = t, OutputAlias = alias, Attribute = logicalName }); } else { groups.Add(new SimpleValueGroup() { OutputAlias = alias, Attribute = logicalName }); } } else { var agrFn = attr.GetAttribute("aggregate")?.Value; if (string.IsNullOrEmpty(agrFn)) { throw new Exception("Attributes must have be aggregated or grouped by when using aggregation"); } FetchAggregate newAgr = null; switch (agrFn?.ToLower()) { case "count": newAgr = new CountAggregate(); break; case "countcolumn": if (attr.IsAttributeTrue("distinct")) { newAgr = new CountDistinctAggregate(); } else { newAgr = new CountColumnAggregate(); } break; case "min": newAgr = new MinAggregate(); break; case "max": newAgr = new MaxAggregate(); break; case "avg": newAgr = new AvgAggregate(); break; case "sum": newAgr = new SumAggregate(); break; default: throw new Exception("Unknown aggregate function '" + agrFn + "'"); } newAgr.OutputAlias = alias; newAgr.Attribute = logicalName; aggregates.Add(newAgr); } } List <Entity> aggregateResult; if (groups.Any()) { aggregateResult = ProcessGroupedAggregate(entityName, resultOfQuery, aggregates, groups); } else { aggregateResult = new List <Entity>(); var ent = ProcessAggregatesForSingleGroup(entityName, resultOfQuery, aggregates); aggregateResult.Add(ent); } return(OrderAggregateResult(xmlDoc, aggregateResult.AsQueryable())); }