protected void BeforeEachBenchmark(int recordCount) { var collection = new ServiceCollection(); collection.AddOData(); collection.AddODataQueryFilter(); _provider = collection.BuildServiceProvider(); var routeBuilder = new RouteBuilder(Mock.Of <IApplicationBuilder>(x => x.ApplicationServices == _provider)); routeBuilder.EnableDependencyInjection(); _oDataRequestHelper = new ODataRequestHelper(); _edmEntityTypeSettings = GetEdmEntityTypeSettings(); _httpContext = new DefaultHttpContext(); _genericEntityRepository = new GenericEntityRepository(recordCount); _oDataRequestHelper.GetEdmModel(_httpContext.Request, _edmEntityTypeSettings, EdmNamespaceName); }
public IEdmModel GetEdmModel(HttpRequest request, EdmEntityTypeSettings edmEntityTypeSettings, string namespaceName) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (edmEntityTypeSettings == null) { throw new ArgumentNullException(nameof(edmEntityTypeSettings)); } if (string.IsNullOrWhiteSpace(namespaceName)) { throw new ArgumentNullException(nameof(namespaceName)); } string modelName = edmEntityTypeSettings.RouteName.ToLowerInvariant(); var odataContext = new ODataContext(); EdmModel model = new EdmModel(); EdmEntityContainer container = new EdmEntityContainer(namespaceName, "container"); model.AddElement(container); var edmEntityType = new EdmEntityType(namespaceName, modelName); foreach (var property in edmEntityTypeSettings.Properties) { if (property.PropertyName.Equals("id", StringComparison.OrdinalIgnoreCase)) { if (property.IsNullable.HasValue) { edmEntityType.AddKeys(edmEntityType.AddStructuralProperty(property.PropertyName, property.GetEdmPrimitiveTypeKind(), property.IsNullable.Value)); } else { edmEntityType.AddKeys(edmEntityType.AddStructuralProperty(property.PropertyName, property.GetEdmPrimitiveTypeKind())); } } else { if (property.IsNullable.HasValue) { edmEntityType.AddStructuralProperty(property.PropertyName, property.GetEdmPrimitiveTypeKind(), property.IsNullable.Value); } else { edmEntityType.AddStructuralProperty(property.PropertyName, property.GetEdmPrimitiveTypeKind()); } } } model.AddElement(edmEntityType); container.AddEntitySet(modelName, edmEntityType); //Set the context odataContext.Settings = new List <EdmEntityTypeSettings> { edmEntityTypeSettings }; odataContext.Path = new ODataPath(); odataContext.EdmModel = model; odataContext.EdmEntityTypeReference = new EdmEntityTypeReference(edmEntityType, true); odataContext.EdmCollectionType = new EdmCollectionType(odataContext.EdmEntityTypeReference); odataContext.ActualRouteName = modelName; request.HttpContext.Items.Add(odataContextKey, odataContext); return(model); }
public IEnumerable <IEdmEntityObject> ApplyFilter(EdmEntityTypeSettings sourceEdmSetting, IEnumerable <IEdmEntityObject> items, SingleValueNode filter, string commandPrefix = "") { //var typeSetting = sourceEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName == attributeName); //if (typeSetting == null) // throw new InvalidPropertyException("Filter", attributeName); SingleValueNode right = null; SingleValueNode left = null; BinaryOperatorKind opr = BinaryOperatorKind.Equal; QueryNodeKind kind = QueryNodeKind.None; QueryNode param1 = null; QueryNode param2 = null; string function = string.Empty; if (filter.Kind == QueryNodeKind.UnaryOperator) { if (((UnaryOperatorNode)filter).OperatorKind == UnaryOperatorKind.Not) { commandPrefix = "not"; kind = ((UnaryOperatorNode)filter).Kind; right = ((UnaryOperatorNode)filter).Operand; } } else if (filter.Kind == QueryNodeKind.Convert && ((ConvertNode)filter).Source.Kind == QueryNodeKind.UnaryOperator) { if (((UnaryOperatorNode)((ConvertNode)filter).Source).OperatorKind == UnaryOperatorKind.Not) { commandPrefix = "not"; kind = ((UnaryOperatorNode)((ConvertNode)filter).Source).Kind; right = ((UnaryOperatorNode)((ConvertNode)filter).Source).Operand; } } else if (filter.Kind == QueryNodeKind.BinaryOperator) { right = ((BinaryOperatorNode)filter).Right; left = ((BinaryOperatorNode)filter).Left; opr = ((BinaryOperatorNode)filter).OperatorKind; kind = ((BinaryOperatorNode)filter).Kind; } else if (filter.Kind == QueryNodeKind.Convert && ((ConvertNode)filter).Source.Kind == QueryNodeKind.BinaryOperator) { right = ((BinaryOperatorNode)((ConvertNode)filter).Source).Right; left = ((BinaryOperatorNode)((ConvertNode)filter).Source).Left; opr = ((BinaryOperatorNode)((ConvertNode)filter).Source).OperatorKind; kind = ((ConvertNode)filter).Source.Kind; } else if (filter.Kind == QueryNodeKind.SingleValueFunctionCall) { kind = filter.Kind; param1 = ((SingleValueFunctionCallNode)filter).Parameters.ElementAt(0); param2 = ((SingleValueFunctionCallNode)filter).Parameters.ElementAt(1); function = ((SingleValueFunctionCallNode)filter).Name; } else if (filter.Kind == QueryNodeKind.Convert) { if (((ConvertNode)filter).Source.Kind == QueryNodeKind.SingleValueFunctionCall) { kind = ((ConvertNode)filter).Source.Kind; param1 = ((SingleValueFunctionCallNode)((ConvertNode)filter).Source).Parameters.ElementAt(0); param2 = ((SingleValueFunctionCallNode)((ConvertNode)filter).Source).Parameters.ElementAt(1); function = ((SingleValueFunctionCallNode)((ConvertNode)filter).Source).Name; } } if (kind == QueryNodeKind.BinaryOperator || kind == QueryNodeKind.UnaryOperator || kind == QueryNodeKind.SingleValueFunctionCall) { if (opr == BinaryOperatorKind.And) { items = ApplyFilter(sourceEdmSetting, items, left).Intersect(ApplyFilter(sourceEdmSetting, items, right)); } else if (opr == BinaryOperatorKind.Or) { items = ApplyFilter(sourceEdmSetting, items, left).Union(ApplyFilter(sourceEdmSetting, items, right)); } else if (function == "startswith" || function == "endswith" || function == "contains") { var param1AttributeName = GetParamAttributeName(param1); var param2AttributeName = GetParamAttributeName(param2); var param1AttributeValue = GetPropertyValue(param1); var param2AttributeValue = GetPropertyValue(param2); var param1AttributeType = GetDataType(param1); var param2AttributeType = GetDataType(param2); switch (commandPrefix + function) { case "startswith": items = items.Where(x => GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().StartsWith(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; case "endswith": items = items.Where(x => GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().EndsWith(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; case "contains": items = items.Where(x => GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().Contains(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; case "notstartswith": items = items.Where(x => !GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().StartsWith(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; case "notendswith": items = items.Where(x => !GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().EndsWith(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; case "notcontains": items = items.Where(x => !GetPropertyValue(x, param1AttributeName, param1AttributeValue).ToString().Contains(GetPropertyValue(x, param2AttributeName, param2AttributeValue).ToString())); break; } } else if (commandPrefix == "not") { items = ApplyFilter(sourceEdmSetting, items, right, commandPrefix); } else if (opr == BinaryOperatorKind.Equal || opr == BinaryOperatorKind.NotEqual || opr == BinaryOperatorKind.GreaterThan || opr == BinaryOperatorKind.GreaterThanOrEqual || opr == BinaryOperatorKind.LessThan || opr == BinaryOperatorKind.LessThanOrEqual) { var leftAttributeName = GetAttributeName(left); var rightAttributeName = GetAttributeName(right); var leftAttributeValue = GetPropertyValue(left); var rightAttributeValue = GetPropertyValue(right); var leftAttributeType = GetDataType(left); var rightAttributeType = GetDataType(right); switch (opr) { case BinaryOperatorKind.Equal: if (leftAttributeType == EdmPrimitiveTypeKind.DateTimeOffset) { items = DateEquals(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType, left); } else { items = items.Where(x => GetPropertyValue(x, leftAttributeName, leftAttributeValue).Equals(GetPropertyValue(x, rightAttributeName, rightAttributeValue))); } break; case BinaryOperatorKind.NotEqual: if (leftAttributeType == EdmPrimitiveTypeKind.DateTimeOffset) { items = DateNotEquals(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType, left); } else { items = items.Where(x => !(GetPropertyValue(x, leftAttributeName, leftAttributeValue).Equals(GetPropertyValue(x, rightAttributeName, rightAttributeValue)))); } break; case BinaryOperatorKind.GreaterThan: items = GreaterThan(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType); break; case BinaryOperatorKind.GreaterThanOrEqual: items = GreaterThanOrEqual(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType); break; case BinaryOperatorKind.LessThan: items = LesserThan(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType); break; case BinaryOperatorKind.LessThanOrEqual: items = LesserThanOrEqual(items, leftAttributeName, rightAttributeName, leftAttributeValue, rightAttributeValue, leftAttributeType); break; } } } return(items); }
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); }
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"); }
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"); } } } }