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 = ExpressionBinderBase.GetPropertyExpression(source, propertyName); Type nullablePropertyType = propertyValue.Type.ToNullable(); Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue); if (filterClause != null) { var 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; 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.RequestContainer); filterResult = Expression.Call( ExpressionHelperMethods.QueryableWhereGeneric.MakeGenericMethod(clrElementType), filterSource, filterPredicate); nullablePropertyType = filterResult.Type; } else if (_settings.HandleReferenceNavigationPropertyExpandFilter) { var filterLambdaExpression = FilterBinder.Bind(null, filterClause, clrElementType, _context.RequestContainer) as LambdaExpression; if (filterLambdaExpression == null) { throw new ODataException(Error.Format(SRResources.ExpandFilterExpressionNotLambdaExpression, property.Name, nameof(LambdaExpression))); } var filterParameter = filterLambdaExpression.Parameters.First(); var predicateExpression = new ReferenceNavigationPropertyExpandFilterVisitor(filterParameter, nullablePropertyValue).Visit(filterLambdaExpression.Body); // predicateExpression == true ? nullablePropertyValue : null filterResult = Expression.Condition( test: predicateExpression, ifTrue: nullablePropertyValue, ifFalse: Expression.Constant(value: null, type: nullablePropertyType)); } if (_settings.HandleNullPropagation == HandleNullPropagationOption.True) { // 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) { // 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); }
private Expression CreatePropertyAccessExpression(Expression source, IEdmProperty property, string propertyPath = null) { string propertyName = EdmLibHelpers.GetClrPropertyName(property, Model); propertyPath = propertyPath ?? propertyName; if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type) && source != this._lambdaParameter) { Expression cleanSource = RemoveInnerNullPropagation(source); Expression propertyAccessExpression = null; propertyAccessExpression = GetFlattenedPropertyExpression(propertyPath) ?? Expression.Property(cleanSource, propertyName); // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property Expression ifFalse = ToNullable(ConvertNonStandardPrimitives(propertyAccessExpression)); return (Expression.Condition( test: Expression.Equal(source, NullConstant), ifTrue: Expression.Constant(null, ifFalse.Type), ifFalse: ifFalse)); } else { return(GetFlattenedPropertyExpression(propertyPath) ?? ConvertNonStandardPrimitives(ExpressionBinderBase.GetPropertyExpression(source, propertyName))); } }