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);
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #5
0
        // 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));
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #7
0
        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);
        }