public ParseContext Parse(ODataUriParser parser, ParseContext sourceParseContext) { SourceParseContext = sourceParseContext; var targetParseContext = new ParseContext(); var targetQueryableSourceEntities = new List <Dictionary <string, object> >(); var sourceEdmSetting = sourceParseContext.EdmEntityTypeSettings.FirstOrDefault(); var targetEdmSetting = new EdmEntityTypeSettings() { RouteName = "Groups", Personas = sourceEdmSetting.Personas, Properties = new List <EdmEntityTypePropertySetting>() }; var latestStateDictionary = new Dictionary <string, object>(); var edmEntityType = new EdmEntityType(EdmNamespaceName, "Groups"); //This may only be used if we client uses Custom.List as aggregation var edmComplexType = new EdmComplexType(EdmNamespaceName, "List"); var aggregatePropList = new Dictionary <string, AggregateExpression>(); var applyClause = parser.ParseApply(); //We support only single transformation if (applyClause.Transformations.Count() > 1) { throw new FeatureNotSupportedException(ApplyParser, "Multiple Transformations"); } if (applyClause.Transformations.Count() == 0) { throw new FeatureNotSupportedException(ApplyParser, "Zero Transformations"); } foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.GroupBy) { var transform = (GroupByTransformationNode)transformation; ///Add all the grouping properties foreach (var groupingProperty in transform.GroupingProperties) { var sourceProperty = sourceEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName.Equals(groupingProperty.Name)); edmEntityType.AddStructuralProperty(groupingProperty.Name, groupingProperty.TypeReference.PrimitiveKind()); targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = sourceProperty.PropertyName, PropertyType = sourceProperty.PropertyType, IsNullable = sourceProperty.IsNullable }); } //Add all the aggregate properties if (transform.ChildTransformations != null) { var aggregationProperties = (AggregateTransformationNode)transform.ChildTransformations; AddAggregationPropertiesToModel(aggregationProperties , sourceEdmSetting, edmEntityType, aggregatePropList, targetEdmSetting , edmComplexType, latestStateDictionary); } //Register these dynamic types to model sourceParseContext.Model.AddElement(edmEntityType); sourceParseContext.Model.AddElement(edmComplexType); ((EdmEntityContainer)sourceParseContext.Model.EntityContainer).AddEntitySet("Groups", edmEntityType); var fields = transform.GroupingProperties.Select(p => p.Name).ToList(); var groups = sourceParseContext.QueryableSourceEntities .GroupBy(r => fields.ToDictionary(c => c, c => r[c]), new CustomEqualityComparer()); var entityRef = new EdmEntityTypeReference(edmEntityType, true); var collectionRef = new EdmCollectionTypeReference(new EdmCollectionType(entityRef)); var collection = new EdmEntityObjectCollection(collectionRef); latestStateDictionary.Add(RequestFilterConstants.GetEntityTypeKeyName(ApplyParser, StepIndex), entityRef); foreach (var group in groups) { var targetQueryableDictionary = new Dictionary <string, object>(); var obj = new EdmEntityObject(edmEntityType); foreach (var prop in fields) { var value = group.Key[prop]; obj.TrySetPropertyValue(prop, value); targetQueryableDictionary.Add(prop, value); } AddAggregationPropertyValuesToModel(targetQueryableDictionary, obj, group, edmComplexType, aggregatePropList); collection.Add(obj); targetQueryableSourceEntities.Add(targetQueryableDictionary); } targetParseContext.Result = collection; targetParseContext.Model = sourceParseContext.Model; targetParseContext.QueryableSourceEntities = targetQueryableSourceEntities; targetParseContext.EdmEntityTypeSettings = new List <EdmEntityTypeSettings> { targetEdmSetting }; targetParseContext.LatestStateDictionary = latestStateDictionary; return(targetParseContext); } else if (transformation.Kind == TransformationNodeKind.Aggregate) { var targetQueryableDictionary = new Dictionary <string, object>(); var obj = new EdmEntityObject(edmEntityType); var aggregationProperties = (AggregateTransformationNode)transformation; AddAggregationPropertiesToModel(aggregationProperties , sourceEdmSetting, edmEntityType, aggregatePropList, targetEdmSetting , edmComplexType, latestStateDictionary); //Register these dynamic types to model sourceParseContext.Model.AddElement(edmEntityType); sourceParseContext.Model.AddElement(edmComplexType); var entityRef = new EdmEntityTypeReference(edmEntityType, true); var collectionRef = new EdmCollectionTypeReference(new EdmCollectionType(entityRef)); var collection = new EdmEntityObjectCollection(collectionRef); latestStateDictionary.Add(RequestFilterConstants.GetEntityTypeKeyName(ApplyParser, StepIndex), entityRef); AddAggregationPropertyValuesToModel(targetQueryableDictionary, obj, sourceParseContext.QueryableSourceEntities, edmComplexType, aggregatePropList); collection.Add(obj); targetQueryableSourceEntities.Add(targetQueryableDictionary); targetParseContext.Result = collection; targetParseContext.Model = sourceParseContext.Model; targetParseContext.QueryableSourceEntities = targetQueryableSourceEntities; targetParseContext.EdmEntityTypeSettings = new List <EdmEntityTypeSettings> { targetEdmSetting }; targetParseContext.LatestStateDictionary = latestStateDictionary; return(targetParseContext); } else { throw new FeatureNotSupportedException(ApplyParser, $"Transformation Kind {transformation.Kind} is not supported"); } } throw new FeatureNotSupportedException(ApplyParser, "Invalid Apply Clause"); }