/// <summary> /// Translate an ExpandedReferenceSelectItem /// </summary> /// <param name="item">the item to Translate</param> /// <returns>Defined by the implementer</returns> public override string Translate(ExpandedReferenceSelectItem item) { NodeToStringBuilder nodeToStringBuilder = new NodeToStringBuilder(); string currentExpandClause = String.Join("/", item.PathToNavigationProperty.WalkWith(PathSegmentToStringTranslator.Instance).ToArray()); string res = string.Empty; if (item.FilterOption != null) { res += "$filter=" + nodeToStringBuilder.TranslateFilterClause(item.FilterOption); } if (item.OrderByOption != null) { res += string.IsNullOrEmpty(res) ? null : ";"; res += "$orderby=" + nodeToStringBuilder.TranslateOrderByClause(item.OrderByOption); } if (item.TopOption != null) { res += string.IsNullOrEmpty(res) ? null : ";"; res += "$top=" + item.TopOption.ToString(); } if (item.SkipOption != null) { res += string.IsNullOrEmpty(res) ? null : ";"; res += "$skip=" + item.SkipOption.ToString(); } if (item.CountOption != null) { res += string.IsNullOrEmpty(res) ? null : ";"; res += "$count"; res += ExpressionConstants.SymbolEqual; if (item.CountOption == true) { res += ExpressionConstants.KeywordTrue; } else { res += ExpressionConstants.KeywordFalse; } } if (item.SearchOption != null) { res += string.IsNullOrEmpty(res) ? null : ";"; res += "$search"; res += ExpressionConstants.SymbolEqual; res += nodeToStringBuilder.TranslateSearchClause(item.SearchOption); } return(string.Concat(currentExpandClause, string.IsNullOrEmpty(res) ? null : string.Concat(ExpressionConstants.SymbolOpenParen, res, ExpressionConstants.SymbolClosedParen))); }
/// <summary> /// Initializes a new instance of the <see cref="ODataSerializerContext"/> class for nested resources. /// </summary> /// <param name="resource">The resource whose navigation property is being expanded.</param> /// <param name="edmProperty">The complex property being nested or the navigation property being expanded. /// If the resource property is the dynamic complex, the resource property is null. /// </param> /// <param name="queryContext">The <see cref="ODataQueryContext"/> for the navigation property being expanded.</param> /// <param name="expandedItem">The <see cref="ExpandedReferenceSelectItem"/> for the navigation property being expanded.></param> internal ODataSerializerContext(ResourceContext resource, IEdmProperty edmProperty, ODataQueryContext queryContext, ExpandedReferenceSelectItem expandedItem) { if (resource == null) { throw Error.ArgumentNull("resource"); } // Clone the resource's context. Use a helper function so it can // handle platform-specific differences in ODataSerializerContext. ODataSerializerContext context = resource.SerializerContext; this.CopyPlatformSpecificProperties(context); Model = context.Model; Path = context.Path; RootElementName = context.RootElementName; SkipExpensiveAvailabilityChecks = context.SkipExpensiveAvailabilityChecks; MetadataLevel = context.MetadataLevel; Items = context.Items; ExpandReference = context.ExpandReference; QueryContext = queryContext; ExpandedResource = resource; // parent resource CurrentExpandedSelectItem = expandedItem; var expandedNavigationSelectItem = expandedItem as ExpandedNavigationSelectItem; if (expandedNavigationSelectItem != null) { SelectExpandClause = expandedNavigationSelectItem.SelectAndExpand; } EdmProperty = edmProperty; // should be nested property Queue <IEdmProperty> parentPropertiesInPath = context.PropertiesInPath != null ? context.PropertiesInPath : new Queue <IEdmProperty>(); parentPropertiesInPath.Enqueue(edmProperty); PropertiesInPath = parentPropertiesInPath; if (context.NavigationSource != null) { IEdmNavigationProperty navigationProperty = edmProperty as IEdmNavigationProperty; if (navigationProperty != null) { NavigationSource = context.NavigationSource.FindNavigationTarget(navigationProperty); } else { NavigationSource = context.NavigationSource; } } }
private void ValidateExpandReferenceItem(ExpandedReferenceSelectItem expandItem, ODataUrlValidationContext validationContext) { ValidateItem(expandItem, validationContext); foreach (ODataPathSegment segment in expandItem.PathToNavigationProperty) { ValidatePathSegment(segment, validationContext); } ValidateFilterClause(expandItem.FilterOption, validationContext); ValidateOrderByClause(expandItem.OrderByOption, validationContext); ValidateSearchClause(expandItem.SearchOption, validationContext); // todo: apply and compute are also defined on ExpandReferenceItem, but should only be valid on ExpandItem // add rule to fail if these are found on ExpandReferenceItem that is not ExpandItem? }
public OeNavigationEntryFactory( IEdmEntitySetBase entitySet, OePropertyAccessor[] accessors, OePropertyAccessor[]?skipTokenAccessors, IReadOnlyList <OeNavigationEntryFactory> navigationLinks, LambdaExpression?linkAccessor, IEdmNavigationProperty edmNavigationProperty, ExpandedReferenceSelectItem navigationSelectItem, bool nextLink) : base(entitySet, accessors, skipTokenAccessors, navigationLinks, linkAccessor) { EdmNavigationProperty = edmNavigationProperty; NavigationSelectItem = navigationSelectItem; NextLink = nextLink; }
/// <summary> /// Handle an ExpandedReferenceSelectItem /// </summary> /// <param name="item">the item to Handle</param> public virtual void Handle(ExpandedReferenceSelectItem item) { throw new NotImplementedException(); }
private void BuildExpansions(IEnumerable <SelectItem> selectedItems, HashSet <IEdmNavigationProperty> allNavigationProperties) { foreach (SelectItem selectItem in selectedItems) { ExpandedReferenceSelectItem expandReferenceItem = selectItem as ExpandedReferenceSelectItem; if (expandReferenceItem != null) { ValidatePathIsSupportedForExpand(expandReferenceItem.PathToNavigationProperty); NavigationPropertySegment navigationSegment = (NavigationPropertySegment)expandReferenceItem.PathToNavigationProperty.LastSegment; IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty; int propertyCountInPath = expandReferenceItem.PathToNavigationProperty.OfType <PropertySegment>().Count(); bool numberOfPropertiesInPathMatch = (propertyCountInPath > 0 && PropertiesInPath != null && PropertiesInPath.Count == propertyCountInPath) || propertyCountInPath < 1; if (numberOfPropertiesInPathMatch && allNavigationProperties.Contains(navigationProperty)) { ExpandedNavigationSelectItem expandItem = selectItem as ExpandedNavigationSelectItem; if (expandItem != null) { if (!ExpandedProperties.ContainsKey(navigationProperty)) { ExpandedProperties.Add(navigationProperty, expandItem); } else { ExpandedProperties[navigationProperty] = expandItem; } } else { ReferencedNavigationProperties.Add(navigationProperty); } } else { //This is the case where the navigation property is not on the current type. We need to propagate the expand item to deeper SelectExpandNode. IEdmStructuralProperty complexProperty = FindNextPropertySegment(expandReferenceItem.PathToNavigationProperty); if (complexProperty != null) { SelectExpandClause newClause; if (ExpandedPropertiesOnSubChildren.ContainsKey(complexProperty)) { SelectExpandClause oldClause = ExpandedPropertiesOnSubChildren[complexProperty].SelectAndExpand; newClause = new SelectExpandClause( oldClause.SelectedItems.Concat(new SelectItem[] { expandReferenceItem }), false); ExpandedNavigationSelectItem newItem = new ExpandedNavigationSelectItem(expandReferenceItem.PathToNavigationProperty, navigationSegment.NavigationSource, newClause); ExpandedPropertiesOnSubChildren[complexProperty] = newItem; } else { newClause = new SelectExpandClause(new SelectItem[] { expandReferenceItem }, false); ExpandedNavigationSelectItem newItem = new ExpandedNavigationSelectItem(expandReferenceItem.PathToNavigationProperty, navigationSegment.NavigationSource, newClause); ExpandedPropertiesOnSubChildren.Add(complexProperty, newItem); } } } } } }
private static FilterClause?GetFilter(IEdmModel edmModel, OeEntryFactory entryFactory, ExpandedReferenceSelectItem item, Object?value) { SingleValueNode filterExpression; ResourceRangeVariableReferenceNode refNode; var segment = (NavigationPropertySegment)item.PathToNavigationProperty.LastSegment; IEdmNavigationProperty navigationProperty = segment.NavigationProperty; if (navigationProperty.ContainsTarget) { ModelBuilder.ManyToManyJoinDescription joinDescription = edmModel.GetManyToManyJoinDescription(navigationProperty); navigationProperty = joinDescription.JoinNavigationProperty.Partner; IEdmEntitySet joinNavigationSource = OeEdmClrHelper.GetEntitySet(edmModel, joinDescription.JoinNavigationProperty); ResourceRangeVariableReferenceNode joinRefNode = OeEdmClrHelper.CreateRangeVariableReferenceNode(joinNavigationSource, "d"); IEdmEntitySet targetNavigationSource = OeEdmClrHelper.GetEntitySet(edmModel, joinDescription.TargetNavigationProperty); ResourceRangeVariableReferenceNode targetRefNode = OeEdmClrHelper.CreateRangeVariableReferenceNode(targetNavigationSource); var anyNode = new AnyNode(new Collection <RangeVariable>() { joinRefNode.RangeVariable, targetRefNode.RangeVariable }, joinRefNode.RangeVariable) { Source = new CollectionNavigationNode(targetRefNode, joinDescription.TargetNavigationProperty.Partner, null), Body = OeExpressionHelper.CreateFilterExpression(joinRefNode, GetKeys(navigationProperty.PrincipalProperties(), navigationProperty.DependentProperties())) }; refNode = targetRefNode; filterExpression = anyNode; } else { IEnumerable <IEdmStructuralProperty> parentKeys; IEnumerable <IEdmStructuralProperty> childKeys; if (navigationProperty.IsPrincipal()) { parentKeys = navigationProperty.Partner.PrincipalProperties(); childKeys = navigationProperty.Partner.DependentProperties(); } else { if (navigationProperty.Type.IsCollection()) { parentKeys = navigationProperty.PrincipalProperties(); childKeys = navigationProperty.DependentProperties(); } else { parentKeys = navigationProperty.DependentProperties(); childKeys = navigationProperty.PrincipalProperties(); } } refNode = OeEdmClrHelper.CreateRangeVariableReferenceNode((IEdmEntitySetBase)segment.NavigationSource); List <KeyValuePair <IEdmStructuralProperty, Object?> > keys = GetKeys(parentKeys, childKeys); if (IsNullKeys(keys)) { return(null); } filterExpression = OeExpressionHelper.CreateFilterExpression(refNode, keys); } if (item.FilterOption != null) { filterExpression = new BinaryOperatorNode(BinaryOperatorKind.And, filterExpression, item.FilterOption.Expression); } return(new FilterClause(filterExpression, refNode.RangeVariable)); List <KeyValuePair <IEdmStructuralProperty, Object?> > GetKeys(IEnumerable <IEdmStructuralProperty> parentKeys, IEnumerable <IEdmStructuralProperty> childKeys) { var keys = new List <KeyValuePair <IEdmStructuralProperty, Object?> >(); IEnumerator <IEdmStructuralProperty> childKeyEnumerator = childKeys.GetEnumerator(); foreach (IEdmStructuralProperty parentKey in parentKeys) { childKeyEnumerator.MoveNext(); Object?keyValue = entryFactory.GetAccessorByName(parentKey.Name).GetValue(value); keys.Add(new KeyValuePair <IEdmStructuralProperty, Object?>(childKeyEnumerator.Current, keyValue)); } return(keys); }
private static ODataUri GetCountODataUri(IEdmModel edmModel, OeEntryFactory entryFactory, ExpandedReferenceSelectItem item, Object?value) { FilterClause?filterClause = GetFilter(edmModel, entryFactory, item, value); if (filterClause == null) { throw new InvalidOperationException("Cannot create count expression"); } var entitytSet = (IEdmEntitySet)((ResourceRangeVariable)filterClause.RangeVariable).NavigationSource; var pathSegments = new ODataPathSegment[] { new EntitySetSegment(entitytSet) { Identifier = entitytSet.Name }, CountSegment.Instance }; return(new ODataUri() { Filter = filterClause, Path = new ODataPath(pathSegments), }); }
/// <summary> /// Handle an ExpandedReferenceSelectItem /// </summary> /// <param name="item">the item to Handle</param> public override void Handle(ExpandedReferenceSelectItem item) { var navigationProperty = (item.PathToNavigationProperty.LastSegment as NavigationPropertySegment).NavigationProperty; this.ExpandedChildElement = this.ParentElement.GetType().GetProperty(navigationProperty.Name).GetValue(this.ParentElement, null); if (this.ExpandedChildElement is IEnumerable) { var entityInstanceType = EdmClrTypeUtils.GetInstanceType(item.NavigationSource.EntityType().FullName()); Expression resultExpression = (this.ExpandedChildElement as IEnumerable).AsQueryable().Expression; if (item.FilterOption != null) { resultExpression = resultExpression.ApplyFilter(entityInstanceType, null, item.FilterOption); } if (item.SearchOption != null) { resultExpression = resultExpression.ApplySearch(entityInstanceType, null, item.SearchOption); } if (item.OrderByOption != null) { resultExpression = resultExpression.ApplyOrderBy(entityInstanceType, null, item.OrderByOption); } if (item.SkipOption.HasValue) { resultExpression = resultExpression.ApplySkip(entityInstanceType, item.SkipOption.Value); } if (item.TopOption.HasValue) { resultExpression = resultExpression.ApplyTop(entityInstanceType, item.TopOption.Value); } Expression<Func<object>> lambda = Expression.Lambda<Func<object>>(resultExpression); Func<object> compiled = lambda.Compile(); this.ExpandedChildElement = compiled() as IEnumerable; } }
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); }
/// <summary> /// Translate an ExpandedReferenceSelectItem /// </summary> /// <param name="item">the item to Translate</param> /// <returns>Defined by the implementer</returns> public virtual T Translate(ExpandedReferenceSelectItem item) { throw new NotImplementedException(); }
private OeNavigationSelectItem AddOrGetNavigationItem(OeNavigationSelectItem parentNavigationItem, ExpandedReferenceSelectItem item, bool isExpand) { IEdmEntitySetBase entitySet = OeEdmClrHelper.GetEntitySet(_edmModel, item); OeNavigationSelectItemKind kind; if (_notSelected) { kind = OeNavigationSelectItemKind.NotSelected; } else if (item is ExpandedNavigationSelectItem expanded && expanded.SelectAndExpand.IsNextLink()) { kind = OeNavigationSelectItemKind.NextLink; }
private async Task WriteNavigationNextLink(OeEntryFactory parentEntryFactory, ExpandedReferenceSelectItem item, Object?value) { Uri?nextPageLink = new OeNextPageLinkBuilder(_queryContext).GetNavigationUri(parentEntryFactory, item, value); if (nextPageLink == null) { return; } var segment = (NavigationPropertySegment)item.PathToNavigationProperty.LastSegment; bool isCollection = segment.NavigationProperty.Type.IsCollection(); var resourceInfo = new ODataNestedResourceInfo() { IsCollection = isCollection, Name = segment.NavigationProperty.Name }; await _writer.WriteStartAsync(resourceInfo).ConfigureAwait(false); if (isCollection) { var resourceSet = new ODataResourceSet() { NextPageLink = nextPageLink }; await _writer.WriteStartAsync(resourceSet).ConfigureAwait(false); await _writer.WriteEndAsync().ConfigureAwait(false); } else { resourceInfo.Url = nextPageLink; } await _writer.WriteEndAsync().ConfigureAwait(false); }