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"); }
public ParseContext Parse(ODataUriParser parser, ParseContext sourceParseContext) { //Select implementation var targetParseContext = new ParseContext(); var targetQueryableSourceEntities = new List <Dictionary <string, object> >(); var sourceEdmSetting = sourceParseContext.EdmEntityTypeSettings.FirstOrDefault(); var targetEdmSetting = new EdmEntityTypeSettings() { RouteName = SelectParser, Personas = sourceEdmSetting.Personas, Properties = new List <EdmEntityTypePropertySetting>() }; var selectExpandClause = parser.ParseSelectAndExpand(); var edmEntityType = new EdmEntityType(EdmNamespaceName, SelectParser); var latestStateDictionary = new Dictionary <string, object>(); //Construct the types. For now we support non-nested primitive types only. Everything else is an exception for now. var propertyList = new List <string>(); foreach (var item in selectExpandClause.SelectedItems) { switch (item) { case PathSelectItem pathSelectItem: IEnumerable <ODataPathSegment> segments = pathSelectItem.SelectedPath; var firstPropertySegment = segments.FirstOrDefault(); if (firstPropertySegment != null) { var typeSetting = sourceEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName == firstPropertySegment.Identifier); propertyList.Add(firstPropertySegment.Identifier); if (typeSetting.GetEdmPrimitiveTypeKind() != EdmPrimitiveTypeKind.None) { var edmPrimitiveType = typeSetting.GetEdmPrimitiveTypeKind(); if (typeSetting.IsNullable.HasValue) { edmEntityType.AddStructuralProperty(firstPropertySegment.Identifier, edmPrimitiveType, typeSetting.IsNullable.Value); } else { edmEntityType.AddStructuralProperty(firstPropertySegment.Identifier, edmPrimitiveType); } targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = typeSetting.PropertyName, PropertyType = typeSetting.PropertyType, IsNullable = typeSetting.IsNullable }); } else { //We are doing $select on a property which is of type list. Which means if (typeSetting.PropertyType == "List") { var edmComplexType = GetEdmComplexTypeReference(sourceParseContext); edmEntityType.AddStructuralProperty(typeSetting.PropertyName, new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(edmComplexType, true)))); targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = typeSetting.PropertyName, PropertyType = typeSetting.PropertyType, IsNullable = typeSetting.IsNullable }); } else { throw new FeatureNotSupportedException(SelectParser, $"Invalid Custom Selection-{typeSetting.PropertyName}-{typeSetting.PropertyType}"); } } } else { throw new FeatureNotSupportedException(SelectParser, "Empty Path Segments"); } break; case WildcardSelectItem wildcardSelectItem: throw new FeatureNotSupportedException(SelectParser, "WildcardSelect"); case ExpandedNavigationSelectItem expandedNavigationSelectItem: throw new FeatureNotSupportedException(SelectParser, "ExpandedNavigation"); case ExpandedReferenceSelectItem expandedReferenceSelectItem: throw new FeatureNotSupportedException(SelectParser, "ExpandedReference"); case NamespaceQualifiedWildcardSelectItem namespaceQualifiedWildcardSelectItem: throw new FeatureNotSupportedException(SelectParser, "NamespaceQualifiedWildcard"); } } //Register these dynamic types to model sourceParseContext.Model.AddElement(edmEntityType); ((EdmEntityContainer)sourceParseContext.Model.EntityContainer).AddEntitySet("Select", edmEntityType); //Construct the data var entityReferenceType = new EdmEntityTypeReference(edmEntityType, true); var collectionRef = new EdmCollectionTypeReference(new EdmCollectionType(entityReferenceType)); var collection = new EdmEntityObjectCollection(collectionRef); var filteredQueryableEntityList = sourceParseContext.QueryableSourceEntities.Select(p => p.Where(p => propertyList.Contains(p.Key))); latestStateDictionary.Add(RequestFilterConstants.GetEntityTypeKeyName(SelectParser, StepIndex), entityReferenceType); foreach (var entity in filteredQueryableEntityList) { var entityDictionary = new Dictionary <string, object>(); var obj = new EdmEntityObject(edmEntityType); foreach (var propertyKey in propertyList) { var setting = targetEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName.Equals(propertyKey)); var data = entity.FirstOrDefault(property => property.Key.Equals(propertyKey)); //This condition is when the type of selected property is a primitive type if (setting.GetEdmPrimitiveTypeKind() != EdmPrimitiveTypeKind.None) { var propertyValue = !data.Equals(default(KeyValuePair <string, object>)) ? data.Value : null; obj.TrySetPropertyValue(propertyKey, propertyValue); entityDictionary.Add(propertyKey, propertyValue); } else { switch (setting.PropertyType) { case "List": //TODO: There is scope for perf improvement //We can re-use the previous constructed list instead of constructing one from scratch. var subList = (List <Dictionary <string, object> >)data.Value; var subListContext = GetList(subList, GetEdmComplexTypeReference(sourceParseContext)); obj.TrySetPropertyValue(propertyKey, subListContext.Result); entityDictionary.Add(propertyKey, subListContext.QueryAbleResult); break; } } } collection.Add(obj); targetQueryableSourceEntities.Add(entityDictionary); } targetParseContext.Result = collection; targetParseContext.QueryableSourceEntities = targetQueryableSourceEntities; targetParseContext.Model = sourceParseContext.Model; targetParseContext.EdmEntityTypeSettings = new List <EdmEntityTypeSettings> { targetEdmSetting }; targetParseContext.LatestStateDictionary = latestStateDictionary; return(targetParseContext); }
private void AddAggregationPropertiesToModel(AggregateTransformationNode aggregationProperties , EdmEntityTypeSettings sourceEdmSetting , EdmEntityType edmEntityType , Dictionary <string, AggregateExpression> aggregatePropList , EdmEntityTypeSettings targetEdmSetting , EdmComplexType edmComplexType , Dictionary <string, object> latestStateDictionary) { foreach (var aggregationExpression in aggregationProperties.AggregateExpressions) { var expr = (AggregateExpression)aggregationExpression; if (expr.Method != AggregationMethod.Custom) { bool? isNullable = null; string propertyAlias = ""; var primitiveKind = expr.Expression.TypeReference.PrimitiveKind(); if (expr.Method == AggregationMethod.VirtualPropertyCount) { var sourceProperty = (CountVirtualPropertyNode)expr.Expression; isNullable = sourceProperty.TypeReference.IsNullable; propertyAlias = !string.IsNullOrWhiteSpace(expr.Alias) ? expr.Alias : sourceProperty.Kind.ToString(); primitiveKind = EdmPrimitiveTypeKind.Int32; } else { var sourceProperty = (SingleValuePropertyAccessNode)expr.Expression; var sourceEdmProperty = sourceEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName.Equals(sourceProperty.Property.Name)); isNullable = sourceEdmProperty.IsNullable; propertyAlias = !string.IsNullOrWhiteSpace(expr.Alias) ? expr.Alias : sourceProperty.Property.Name; if (expr.Method == AggregationMethod.Average) { primitiveKind = EdmPrimitiveTypeKind.Double; } if (expr.Method == AggregationMethod.CountDistinct) { primitiveKind = EdmPrimitiveTypeKind.Int32; } } edmEntityType.AddStructuralProperty(propertyAlias, primitiveKind); aggregatePropList.Add(propertyAlias, expr); targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = propertyAlias, PropertyType = GetStringTypeFromEdmPrimitiveType(primitiveKind), IsNullable = isNullable }); } else { //Create a list of source type if (expr.MethodDefinition.MethodLabel.Contains(ODataFilterConstants.AggregationMethod_Custom_List, StringComparison.OrdinalIgnoreCase)) { foreach (var property in sourceEdmSetting.Properties) { edmComplexType.AddStructuralProperty(property.PropertyName, property.GetEdmPrimitiveTypeKind()); } var groupItemsPropertyName = !string.IsNullOrWhiteSpace(expr.Alias) ? expr.Alias : "Items"; var complexTypeReference = new EdmComplexTypeReference(edmComplexType, true); edmEntityType.AddStructuralProperty(groupItemsPropertyName, new EdmCollectionTypeReference(new EdmCollectionType(complexTypeReference))); aggregatePropList.Add(groupItemsPropertyName, expr); targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = groupItemsPropertyName, PropertyType = "List", IsNullable = null }); latestStateDictionary.Add(RequestFilterConstants.GetComplexTypeKeyName(ApplyParser, StepIndex), edmComplexType); } else if (expr.MethodDefinition.MethodLabel.Contains(ODataFilterConstants.AggregationMethod_Custom_CountDistinct, StringComparison.OrdinalIgnoreCase) || expr.MethodDefinition.MethodLabel.Contains(ODataFilterConstants.AggregationMethod_Custom_Count, StringComparison.OrdinalIgnoreCase)) { var sourceProperty = (SingleValuePropertyAccessNode)expr.Expression; var primitiveKind = EdmPrimitiveTypeKind.Int32; var countDistinctPropName = !string.IsNullOrWhiteSpace(expr.Alias) ? expr.Alias : sourceProperty.Property.Name; edmEntityType.AddStructuralProperty(countDistinctPropName, primitiveKind); aggregatePropList.Add(countDistinctPropName, expr); targetEdmSetting.Properties.Add(new EdmEntityTypePropertySetting { PropertyName = countDistinctPropName, PropertyType = GetStringTypeFromEdmPrimitiveType(primitiveKind), IsNullable = null }); } else { throw new FeatureNotSupportedException($"{ApplyParser}-Custom Aggregation-{expr.MethodDefinition.MethodLabel}", "Invalid Custom Aggregation"); } } } }