/// <summary> /// Generate an expand item based on an ExpandTermToken /// </summary> /// <param name="tokenIn">the expandTerm token to visit</param> /// <returns>the expand item for this expand term token.</returns> private ExpandedNavigationSelectItem GenerateExpandItem(ExpandTermToken tokenIn) { ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn"); // ensure that we're always dealing with a normalized tree if (tokenIn.PathToNavProp.NextToken != null && !tokenIn.PathToNavProp.IsNamespaceOrContainerQualified()) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingANonNormalizedTree); } PathSegmentToken currentToken = tokenIn.PathToNavProp; IEdmEntityType currentLevelEntityType = this.entityType; List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); PathSegmentToken firstNonTypeToken = currentToken; if (currentToken.IsNamespaceOrContainerQualified()) { pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, ref currentLevelEntityType, out firstNonTypeToken)); } IEdmProperty edmProperty = currentLevelEntityType.FindProperty(firstNonTypeToken.Identifier); if (edmProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.FullName(), currentToken.Identifier)); } IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty; if (currentNavProp == null) { // the server allowed non-navigation, non-stream properties to be expanded, but then ignored them. if (this.Settings.UseWcfDataServicesServerBehavior && !edmProperty.Type.IsStream()) { return(null); } throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationProperty(currentToken.Identifier, currentLevelEntityType.FullName())); } pathSoFar.Add(new NavigationPropertySegment(currentNavProp, /*entitySet*/ null)); ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar); SelectExpandClause subSelectExpand; if (tokenIn.ExpandOption != null) { subSelectExpand = this.GenerateSubExpand(currentNavProp, tokenIn); } else { subSelectExpand = BuildDefaultSubExpand(); } subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption); IEdmEntitySet targetEntitySet = null; if (this.entitySet != null) { targetEntitySet = this.entitySet.FindNavigationTarget(currentNavProp); } // call MetadataBinder to build the filter clause FilterClause filterOption = null; if (tokenIn.FilterOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetEntitySet); FilterBinder filterBinder = new FilterBinder(binder.Bind, binder.BindingState); filterOption = filterBinder.BindFilter(tokenIn.FilterOption); } // call MetadataBinder again to build the orderby clause OrderByClause orderbyOption = null; if (tokenIn.OrderByOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetEntitySet); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); orderbyOption = orderByBinder.BindOrderBy(binder.BindingState, new OrderByToken[] { tokenIn.OrderByOption }); } return(new ExpandedNavigationSelectItem(pathToNavProp, targetEntitySet, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.InlineCountOption, subSelectExpand)); }
private void ProcessTokenAsPath(NonSystemToken tokenIn) { Debug.Assert(tokenIn != null, "tokenIn != null"); List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); IEdmEntityType currentLevelEntityType = this.entityType; // first, walk through all type segments in a row, converting them from tokens into segments. if (tokenIn.IsNamespaceOrContainerQualified()) { PathSegmentToken firstNonTypeToken; pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(tokenIn, this.model, this.maxDepth, ref currentLevelEntityType, out firstNonTypeToken)); Debug.Assert(firstNonTypeToken != null, "Did not get last token."); tokenIn = firstNonTypeToken as NonSystemToken; if (tokenIn == null) { throw new ODataException(ODataErrorStrings.SelectPropertyVisitor_SystemTokenInSelect(firstNonTypeToken.Identifier)); } } // next, create a segment for the first non-type segment in the path. ODataPathSegment lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(tokenIn, this.model, currentLevelEntityType); Debug.Assert(lastSegment != null, "nextSegment != null"); // next, create an ODataPath and add the segments to it. pathSoFar.Add(lastSegment); ODataSelectPath selectedPath = new ODataSelectPath(pathSoFar); var navigationSelection = new PathSelectItem(selectedPath); // next, create a selection item for the path, based on the last segment's type. // TODO: just have PathSelectItem if (lastSegment is NavigationPropertySegment) { bool foundExactExpand = false; bool foundDifferentTypeExpand = false; foreach (var subItem in this.expandClauseToDecorate.Expansion.ExpandItems) { IEdmNavigationProperty subItemNavigationProperty = subItem.PathToNavigationProperty.GetNavigationProperty(); if (subItem.PathToNavigationProperty.Equals(navigationSelection.SelectedPath)) { foundExactExpand = true; if (tokenIn.NextToken == null) { subItem.SelectAndExpand.SetAllSelectionRecursively(); } else { SelectPropertyVisitor nextLevelVisitor = new SelectPropertyVisitor(this.model, subItemNavigationProperty.ToEntityType(), this.maxDepth, subItem.SelectAndExpand); tokenIn.NextToken.Accept(nextLevelVisitor); } } else if (subItem.PathToNavigationProperty.LastSegment.Equals(navigationSelection.SelectedPath.LastSegment)) { foundDifferentTypeExpand = true; } } if (foundDifferentTypeExpand && !foundExactExpand) { throw new ODataException(ODataErrorStrings.SelectPropertyVisitor_DisparateTypeSegmentsInSelectExpand); } if (!foundExactExpand) { // if it has sub-properties selected, then require it to have been expanded. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectionItemBinder_NoExpandForSelectedProperty(tokenIn.Identifier)); } // otherwise just add it to the partial selection. this.expandClauseToDecorate.AddSelectItem(navigationSelection); } else { this.expandClauseToDecorate.InitializeEmptySelection(); } } else { // non-navigation cases do not allow further segments in $select. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectionItemBinder_NonNavigationPathToken); } this.expandClauseToDecorate.AddSelectItem(navigationSelection); } }