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 #2
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);
        }