private Expression CreateTotalCountExpression(Expression source, ExpandedReferenceSelectItem expandItem) { Expression countExpression = Expression.Constant(null, typeof(long?)); if (expandItem.CountOption == null || !expandItem.CountOption.Value) { return(countExpression); } Type elementType; if (!TypeHelper.IsCollection(source.Type, out elementType)) { return(countExpression); } MethodInfo countMethod; if (typeof(IQueryable).IsAssignableFrom(source.Type)) { countMethod = ExpressionHelperMethods.QueryableCountGeneric.MakeGenericMethod(elementType); } else { countMethod = ExpressionHelperMethods.EnumerableCountGeneric.MakeGenericMethod(elementType); } // call Count() method. countExpression = Expression.Call(null, countMethod, new[] { source }); if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // source == null ? null : countExpression return(Expression.Condition( test: Expression.Equal(source, Expression.Constant(null)), ifTrue: Expression.Constant(null, typeof(long?)), ifFalse: ExpressionHelpers.ToNullable(countExpression))); } else { return(countExpression); } }
private Expression BindCountNode(CountNode node) { Expression source = Bind(node.Source); Expression countExpression = Expression.Constant(null, typeof(long?)); Type elementType; if (!TypeHelper.IsCollection(source.Type, out elementType)) { return(countExpression); } MethodInfo countMethod; if (typeof(IQueryable).IsAssignableFrom(source.Type)) { countMethod = ExpressionHelperMethods.QueryableCountGeneric.MakeGenericMethod(elementType); } else { countMethod = ExpressionHelperMethods.EnumerableCountGeneric.MakeGenericMethod(elementType); } // call Count() method. countExpression = Expression.Call(null, countMethod, new[] { source }); if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True) { // source == null ? null : countExpression return(Expression.Condition( test: Expression.Equal(source, Expression.Constant(null)), ifTrue: Expression.Constant(null, typeof(long?)), ifFalse: ExpressionHelpers.ToNullable(countExpression))); } else { return(countExpression); } }
private Expression BuildPropertyContainer(IEdmEntityType elementType, Expression source, Dictionary <IEdmNavigationProperty, ExpandedReferenceSelectItem> propertiesToExpand, ISet <IEdmStructuralProperty> propertiesToInclude, ISet <IEdmStructuralProperty> autoSelectedProperties, bool isSelectingOpenTypeSegments) { IList <NamedPropertyExpression> includedProperties = new List <NamedPropertyExpression>(); foreach (KeyValuePair <IEdmNavigationProperty, ExpandedReferenceSelectItem> kvp in propertiesToExpand) { IEdmNavigationProperty propertyToExpand = kvp.Key; ExpandedReferenceSelectItem expandItem = kvp.Value; SelectExpandClause projection = GetOrCreateSelectExpandClause(kvp); ModelBoundQuerySettings querySettings = EdmLibHelpers.GetModelBoundQuerySettings(propertyToExpand, propertyToExpand.ToEntityType(), _context.Model); Expression propertyName = CreatePropertyNameExpression(elementType, propertyToExpand, source); Expression propertyValue = CreatePropertyValueExpressionWithFilter(elementType, propertyToExpand, source, expandItem.FilterOption); Expression nullCheck = GetNullCheckExpression(propertyToExpand, propertyValue, projection); Expression countExpression = CreateTotalCountExpression(propertyValue, expandItem); // projection can be null if the expanded navigation property is not further projected or expanded. if (projection != null) { int?modelBoundPageSize = querySettings == null ? null : querySettings.PageSize; propertyValue = ProjectAsWrapper(propertyValue, projection, propertyToExpand.ToEntityType(), expandItem.NavigationSource, expandItem, modelBoundPageSize); } NamedPropertyExpression propertyExpression = new NamedPropertyExpression(propertyName, propertyValue); if (projection != null) { if (!propertyToExpand.Type.IsCollection()) { propertyExpression.NullCheck = nullCheck; } else if (_settings.PageSize.HasValue) { propertyExpression.PageSize = _settings.PageSize.Value; } else { if (querySettings != null && querySettings.PageSize.HasValue) { propertyExpression.PageSize = querySettings.PageSize.Value; } } propertyExpression.TotalCount = countExpression; propertyExpression.CountOption = expandItem.CountOption; } includedProperties.Add(propertyExpression); } foreach (IEdmStructuralProperty propertyToInclude in propertiesToInclude) { Expression propertyName = CreatePropertyNameExpression(elementType, propertyToInclude, source); Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source); includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue)); } foreach (IEdmStructuralProperty propertyToInclude in autoSelectedProperties) { Expression propertyName = CreatePropertyNameExpression(elementType, propertyToInclude, source); Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source); includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue) { AutoSelected = true }); } if (isSelectingOpenTypeSegments) { var dynamicPropertyDictionary = EdmLibHelpers.GetDynamicPropertyDictionary(elementType, _model); if (dynamicPropertyDictionary != null) { Expression propertyName = Expression.Constant(dynamicPropertyDictionary.Name); Expression propertyValue = Expression.Property(source, dynamicPropertyDictionary.Name); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // source == null ? null : propertyValue propertyValue = Expression.Condition( test: Expression.Equal(source, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: TypeHelper.ToNullable(propertyValue.Type)), ifFalse: nullablePropertyValue); } else { propertyValue = nullablePropertyValue; } includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue)); } } // create a property container that holds all these property names and values. return(PropertyContainer.CreatePropertyContainer(includedProperties)); }
internal Expression CreatePropertyValueExpressionWithFilter(IEdmEntityType elementType, IEdmProperty property, Expression source, FilterClause filterClause) { Contract.Assert(elementType != null); Contract.Assert(property != null); Contract.Assert(source != null); IEdmEntityType declaringType = property.DeclaringType as IEdmEntityType; Contract.Assert(declaringType != null, "only entity types are projected."); // derived property using cast if (elementType != declaringType) { Type castType = EdmLibHelpers.GetClrType(declaringType, _model); if (castType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, declaringType.FullName())); } source = Expression.TypeAs(source, castType); } string propertyName = EdmLibHelpers.GetClrPropertyName(property, _model); Expression propertyValue = Expression.Property(source, propertyName); Type nullablePropertyType = TypeHelper.ToNullable(propertyValue.Type); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); if (filterClause != null) { bool isCollection = property.Type.IsCollection(); IEdmTypeReference edmElementType = (isCollection ? property.Type.AsCollection().ElementType() : property.Type); Type clrElementType = EdmLibHelpers.GetClrType(edmElementType, _model); if (clrElementType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, edmElementType.FullName())); } Expression filterResult = nullablePropertyValue; ODataQuerySettings querySettings = new ODataQuerySettings() { HandleNullPropagation = HandleNullPropagationOption.True, }; if (isCollection) { Expression filterSource = typeof(IEnumerable).IsAssignableFrom(source.Type.GetProperty(propertyName).PropertyType) ? Expression.Call( ExpressionHelperMethods.QueryableAsQueryable.MakeGenericMethod(clrElementType), nullablePropertyValue) : nullablePropertyValue; // TODO: Implement proper support for $select/$expand after $apply Expression filterPredicate = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings); filterResult = Expression.Call( ExpressionHelperMethods.QueryableWhereGeneric.MakeGenericMethod(clrElementType), filterSource, filterPredicate); nullablePropertyType = filterResult.Type; } else if (_settings.HandleReferenceNavigationPropertyExpandFilter) { LambdaExpression filterLambdaExpression = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings) as LambdaExpression; if (filterLambdaExpression == null) { throw new ODataException(Error.Format(SRResources.ExpandFilterExpressionNotLambdaExpression, property.Name, "LambdaExpression")); } ParameterExpression filterParameter = filterLambdaExpression.Parameters.First(); Expression predicateExpression = new ReferenceNavigationPropertyExpandFilterVisitor(filterParameter, nullablePropertyValue).Visit(filterLambdaExpression.Body); // create expression similar to: 'predicateExpression == true ? nullablePropertyValue : null' filterResult = Expression.Condition( test: predicateExpression, ifTrue: nullablePropertyValue, ifFalse: Expression.Constant(value: null, type: nullablePropertyType)); } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'nullablePropertyValue == null ? null : filterResult' nullablePropertyValue = Expression.Condition( test: Expression.Equal(nullablePropertyValue, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: filterResult); } else { nullablePropertyValue = filterResult; } } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'source == null ? null : propertyValue' propertyValue = Expression.Condition( test: Expression.Equal(source, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: nullablePropertyValue); } else { // need to cast this to nullable as EF would fail while materializing if the property is not nullable and source is null. propertyValue = nullablePropertyValue; } return(propertyValue); }
// Expression: // new NamedProperty<T> { Name = property.Name, Value = property.Value, Next0 = next0, Next1 = next1, .... }. private static Expression CreateNamedPropertyCreationExpression(NamedPropertyExpression property, IList <Expression> expressions) { Contract.Assert(property != null); Contract.Assert(property.Value != null); Type namedPropertyType = GetNamedPropertyType(property, expressions); List <MemberBinding> memberBindings = new List <MemberBinding>(); memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Name"), property.Name)); if (property.PageSize != null || property.CountOption != null) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Collection"), property.Value)); if (property.PageSize != null) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("PageSize"), Expression.Constant(property.PageSize))); } if (property.CountOption != null && property.CountOption.Value) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("TotalCount"), ExpressionHelpers.ToNullable(property.TotalCount))); } } else { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Value"), property.Value)); } for (int i = 0; i < expressions.Count; i++) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Next" + i), expressions[i])); } if (property.NullCheck != null) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("IsNull"), property.NullCheck)); } return(Expression.MemberInit(Expression.New(namedPropertyType), memberBindings)); }
private Expression BindCountNode(CountNode node) { Expression source = Bind(node.Source); Expression countExpression = Expression.Constant(null, typeof(long?)); Type elementType; if (!TypeHelper.IsCollection(source.Type, out elementType)) { return(countExpression); } MethodInfo countMethod; if (typeof(IQueryable).IsAssignableFrom(source.Type)) { countMethod = ExpressionHelperMethods.QueryableCountGeneric.MakeGenericMethod(elementType); } else { countMethod = ExpressionHelperMethods.EnumerableCountGeneric.MakeGenericMethod(elementType); } MethodInfo whereMethod; if (typeof(IQueryable).IsAssignableFrom(source.Type)) { whereMethod = ExpressionHelperMethods.QueryableWhereGeneric.MakeGenericMethod(elementType); } else { whereMethod = ExpressionHelperMethods.EnumerableWhereGeneric.MakeGenericMethod(elementType); } // Bind the inner $filter clause within the $count segment. // e.g Books?$filter=Authors/$count($filter=Id gt 1) gt 1 Expression filterExpression = null; if (node.FilterClause != null) { filterExpression = BindFilterClause(this, node.FilterClause, elementType); // The source expression looks like: $it.Authors // So the generated source expression below will look like: $it.Authors.Where($it => $it.Id > 1) source = Expression.Call(null, whereMethod, new[] { source, filterExpression }); } // append LongCount() method. // The final countExpression with the nested $filter clause will look like: $it.Authors.Where($it => $it.Id > 1).LongCount() // The final countExpression without the nested $filter clause will look like: $it.Authors.LongCount() countExpression = Expression.Call(null, countMethod, new[] { source }); if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True) { // source == null ? null : countExpression return(Expression.Condition( test: Expression.Equal(source, Expression.Constant(null)), ifTrue: Expression.Constant(null, typeof(long?)), ifFalse: ExpressionHelpers.ToNullable(countExpression))); } else { return(countExpression); } }
internal Expression CreatePropertyValueExpressionWithFilter(IEdmStructuredType elementType, IEdmProperty property, Expression source, ExpandedReferenceSelectItem expandItem) { Contract.Assert(elementType != null); Contract.Assert(property != null); Contract.Assert(source != null); FilterClause filterClause = expandItem != null ? expandItem.FilterOption : null; IEdmStructuredType declaringType; IEdmStructuredType currentType = elementType; if (expandItem != null) { foreach (ODataPathSegment segment in expandItem.PathToNavigationProperty) { string currentProperty = String.Empty; PropertySegment propertyAccessPathSegment = segment as PropertySegment; if (propertyAccessPathSegment != null) { IEdmProperty propertyInPath = propertyAccessPathSegment.Property; currentProperty = EdmLibHelpers.GetClrPropertyName(propertyInPath, _model); declaringType = propertyInPath.DeclaringType; Contract.Assert(!String.IsNullOrEmpty(currentProperty), "Property name could not be found on the model."); if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'source == null ? null : propertyValue' if (declaringType != currentType) { Type castType = EdmLibHelpers.GetClrType(property.DeclaringType, _model); if (castType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, property.DeclaringType.FullTypeName())); } source = Expression.TypeAs(source, castType); } Expression propertyExpression = Expression.Property(source, currentProperty); Type nullablePropType = TypeHelper.ToNullable(propertyExpression.Type); source = Expression.Condition( test: Expression.Equal(propertyExpression, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropType), ifFalse: propertyExpression); } else { source = Expression.Property(source, currentProperty); } currentType = propertyInPath.Type.ToStructuredType(); } else { TypeSegment typeSegment = segment as TypeSegment; if (typeSegment != null) { Type castType = EdmLibHelpers.GetClrType(typeSegment.EdmType, _model); source = Expression.TypeAs(source, castType); currentType = typeSegment.EdmType as IEdmStructuredType; } } } } // derived property using cast if (currentType != property.DeclaringType) { Type castType = EdmLibHelpers.GetClrType(property.DeclaringType, _model); if (castType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, property.DeclaringType.FullTypeName())); } source = Expression.TypeAs(source, castType); } string propertyName = EdmLibHelpers.GetClrPropertyName(property, _model); PropertyInfo propertyInfo = source.Type.GetProperty(propertyName); Expression propertyValue = Expression.Property(source, propertyInfo); Type nullablePropertyType = TypeHelper.ToNullable(propertyValue.Type); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); if (filterClause != null) { bool isCollection = property.Type.IsCollection(); IEdmTypeReference edmElementType = (isCollection ? property.Type.AsCollection().ElementType() : property.Type); Type clrElementType = EdmLibHelpers.GetClrType(edmElementType, _model); if (clrElementType == null) { throw new ODataException(Error.Format(SRResources.MappingDoesNotContainResourceType, edmElementType.FullName())); } Expression filterResult = nullablePropertyValue; ODataQuerySettings querySettings = new ODataQuerySettings() { HandleNullPropagation = HandleNullPropagationOption.True, }; if (isCollection) { Expression filterSource = nullablePropertyValue; // TODO: Implement proper support for $select/$expand after $apply Expression filterPredicate = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings); filterResult = Expression.Call( ExpressionHelperMethods.EnumerableWhereGeneric.MakeGenericMethod(clrElementType), filterSource, filterPredicate); nullablePropertyType = filterResult.Type; } else if (_settings.HandleReferenceNavigationPropertyExpandFilter) { LambdaExpression filterLambdaExpression = FilterBinder.Bind(null, filterClause, clrElementType, _context, querySettings) as LambdaExpression; if (filterLambdaExpression == null) { throw new ODataException(Error.Format(SRResources.ExpandFilterExpressionNotLambdaExpression, property.Name, "LambdaExpression")); } ParameterExpression filterParameter = filterLambdaExpression.Parameters.First(); Expression predicateExpression = new ReferenceNavigationPropertyExpandFilterVisitor(filterParameter, nullablePropertyValue).Visit(filterLambdaExpression.Body); // create expression similar to: 'predicateExpression == true ? nullablePropertyValue : null' filterResult = Expression.Condition( test: predicateExpression, ifTrue: nullablePropertyValue, ifFalse: Expression.Constant(value: null, type: nullablePropertyType)); } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'nullablePropertyValue == null ? null : filterResult' nullablePropertyValue = Expression.Condition( test: Expression.Equal(nullablePropertyValue, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: filterResult); } else { nullablePropertyValue = filterResult; } } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // create expression similar to: 'source == null ? null : propertyValue' propertyValue = Expression.Condition( test: Expression.Equal(source, Expression.Constant(value: null)), ifTrue: Expression.Constant(value: null, type: nullablePropertyType), ifFalse: nullablePropertyValue); } else { // need to cast this to nullable as EF would fail while materializing if the property is not nullable and source is null. propertyValue = nullablePropertyValue; } return(propertyValue); }