/// <summary> /// Initialize the Node from <see cref="SelectExpandClause"/> for the given <see cref="IEdmStructuredType"/>. /// </summary> /// <param name="selectExpandClause">The input select and expand clause ($select and $expand).</param> /// <param name="structuredType">The related structural type to select and expand.</param> /// <param name="model">The Edm model.</param> /// <param name="expandedReference">Is expanded reference.</param> private void Initialize(SelectExpandClause selectExpandClause, IEdmStructuredType structuredType, IEdmModel model, bool expandedReference) { if (structuredType == null) { throw Error.ArgumentNull("structuredType"); } if (model == null) { throw Error.ArgumentNull("model"); } IEdmEntityType entityType = structuredType as IEdmEntityType; if (expandedReference) { SelectAllDynamicProperties = false; if (entityType != null) { // only need to include the key properties. SelectedStructuralProperties = new HashSet <IEdmStructuralProperty>(entityType.Key()); } } else { EdmStructuralTypeInfo structuralTypeInfo = new EdmStructuralTypeInfo(model, structuredType); if (selectExpandClause == null) { SelectAllDynamicProperties = true; // includes navigation properties SelectedNavigationProperties = structuralTypeInfo.AllNavigationProperties; // includes all bound actions SelectedActions = structuralTypeInfo.AllActions; // includes all bound functions SelectedFunctions = structuralTypeInfo.AllFunctions; // includes all structural properties if (structuralTypeInfo.AllStructuralProperties != null) { foreach (var property in structuralTypeInfo.AllStructuralProperties) { AddStructuralProperty(property, null); } } } else { BuildSelectExpand(selectExpandClause, structuralTypeInfo); } AdjustSelectNavigationProperties(); } }
/// <summary> /// Build the $select item, it maybe $select=complex/abc, $select=abc, $select=nav, etc. /// </summary> /// <param name="pathSelectItem">The expanded reference select item.</param> /// <param name="currentLevelPropertiesInclude">The current properties to include at current level.</param> /// <param name="structuralTypeInfo">The structural type properties.</param> private void BuildSelectItem(PathSelectItem pathSelectItem, IDictionary <IEdmStructuralProperty, SelectExpandIncludedProperty> currentLevelPropertiesInclude, EdmStructuralTypeInfo structuralTypeInfo) { Contract.Assert(pathSelectItem != null && pathSelectItem.SelectedPath != null); Contract.Assert(currentLevelPropertiesInclude != null); Contract.Assert(structuralTypeInfo != null); // Verify and process the $select=abc/xyz/.... ODataSelectPath selectPath = pathSelectItem.SelectedPath; IList <ODataPathSegment> remainingSegments; ODataPathSegment segment = selectPath.GetFirstNonTypeCastSegment(out remainingSegments); PropertySegment firstPropertySegment = segment as PropertySegment; if (firstPropertySegment != null) { if (structuralTypeInfo.IsStructuralPropertyDefined(firstPropertySegment.Property)) { // $select=abc/xyz/... SelectExpandIncludedProperty newPropertySelectItem; if (!currentLevelPropertiesInclude.TryGetValue(firstPropertySegment.Property, out newPropertySelectItem)) { newPropertySelectItem = new SelectExpandIncludedProperty(firstPropertySegment); currentLevelPropertiesInclude[firstPropertySegment.Property] = newPropertySelectItem; } newPropertySelectItem.AddSubSelectItem(remainingSegments, pathSelectItem); } return; } // If the first segment is not a property segment, // that segment must be the last segment, so the remainging segments should be null. Contract.Assert(remainingSegments == null); NavigationPropertySegment navigationSegment = segment as NavigationPropertySegment; if (navigationSegment != null) { // for example: $select=NavigationProperty or $select=NS.VipCustomer/VipNav if (structuralTypeInfo.IsNavigationPropertyDefined(navigationSegment.NavigationProperty)) { if (SelectedNavigationProperties == null) { SelectedNavigationProperties = new HashSet <IEdmNavigationProperty>(); } SelectedNavigationProperties.Add(navigationSegment.NavigationProperty); } return; } OperationSegment operationSegment = segment as OperationSegment; if (operationSegment != null) { // for example: $select=NS.Operation, or, $select=NS.VipCustomer/NS.Operation AddOperations(operationSegment, structuralTypeInfo.AllActions, structuralTypeInfo.AllFunctions); return; } DynamicPathSegment dynamicPathSegment = segment as DynamicPathSegment; if (dynamicPathSegment != null) { if (SelectedDynamicProperties == null) { SelectedDynamicProperties = new HashSet <string>(); } SelectedDynamicProperties.Add(dynamicPathSegment.Identifier); return; } // In fact, we should never be here, because it's verified above throw new ODataException(Error.Format(SRResources.SelectionTypeNotSupported, segment.GetType().Name)); }
/// <summary> /// Build the $expand item, it maybe $expand=nav, $expand=complex/nav, $expand=nav/$ref, etc. /// </summary> /// <param name="expandReferenceItem">The expanded reference select item.</param> /// <param name="currentLevelPropertiesInclude">The current properties to include at current level.</param> /// <param name="structuralTypeInfo">The structural type properties.</param> private void BuildExpandItem(ExpandedReferenceSelectItem expandReferenceItem, IDictionary <IEdmStructuralProperty, SelectExpandIncludedProperty> currentLevelPropertiesInclude, EdmStructuralTypeInfo structuralTypeInfo) { Contract.Assert(expandReferenceItem != null && expandReferenceItem.PathToNavigationProperty != null); Contract.Assert(currentLevelPropertiesInclude != null); Contract.Assert(structuralTypeInfo != null); // Verify and process the $expand=abc/xyz/nav. ODataExpandPath expandPath = expandReferenceItem.PathToNavigationProperty; IList <ODataPathSegment> remainingSegments; ODataPathSegment segment = expandPath.GetFirstNonTypeCastSegment(out remainingSegments); PropertySegment firstPropertySegment = segment as PropertySegment; if (firstPropertySegment != null) { // for example: $expand=abc/xyz/nav, the remaining segment can't be null // because at least the last navigation property segment is there. Contract.Assert(remainingSegments != null); if (structuralTypeInfo.IsStructuralPropertyDefined(firstPropertySegment.Property)) { SelectExpandIncludedProperty newPropertySelectItem; if (!currentLevelPropertiesInclude.TryGetValue(firstPropertySegment.Property, out newPropertySelectItem)) { newPropertySelectItem = new SelectExpandIncludedProperty(firstPropertySegment); currentLevelPropertiesInclude[firstPropertySegment.Property] = newPropertySelectItem; } newPropertySelectItem.AddSubExpandItem(remainingSegments, expandReferenceItem); } } else { // for example: $expand=nav, or $expand=NS.SubType/nav, the navigation property segment should be the last segment. // So, the remaining segments should be null. Contract.Assert(remainingSegments == null); NavigationPropertySegment firstNavigationSegment = segment as NavigationPropertySegment; Contract.Assert(firstNavigationSegment != null); if (structuralTypeInfo.IsNavigationPropertyDefined(firstNavigationSegment.NavigationProperty)) { // It's not allowed to have mulitple navigation expanded or referenced. // for example: "$expand=nav($top=2),nav($skip=3)" is not allowed and will be merged (or throw exception) at ODL side. ExpandedNavigationSelectItem expanded = expandReferenceItem as ExpandedNavigationSelectItem; if (expanded != null) { if (ExpandedProperties == null) { ExpandedProperties = new Dictionary <IEdmNavigationProperty, ExpandedNavigationSelectItem>(); } ExpandedProperties[firstNavigationSegment.NavigationProperty] = expanded; } else { // $expand=..../nav/$ref if (ReferencedProperties == null) { ReferencedProperties = new Dictionary <IEdmNavigationProperty, ExpandedReferenceSelectItem>(); } ReferencedProperties[firstNavigationSegment.NavigationProperty] = expandReferenceItem; } } } }
/// <summary> /// Build $select and $expand clause /// </summary> /// <param name="selectExpandClause">The select expand clause</param> /// <param name="structuralTypeInfo">The structural type properties.</param> private void BuildSelectExpand(SelectExpandClause selectExpandClause, EdmStructuralTypeInfo structuralTypeInfo) { Contract.Assert(selectExpandClause != null); Contract.Assert(structuralTypeInfo != null); var currentLevelPropertiesInclude = new Dictionary <IEdmStructuralProperty, SelectExpandIncludedProperty>(); // Explicitly set SelectAllDynamicProperties as false, // Below will re-set it as true if it meets the select all condition. SelectAllDynamicProperties = false; foreach (SelectItem selectItem in selectExpandClause.SelectedItems) { // $expand=... ExpandedReferenceSelectItem expandReferenceItem = selectItem as ExpandedReferenceSelectItem; if (expandReferenceItem != null) { BuildExpandItem(expandReferenceItem, currentLevelPropertiesInclude, structuralTypeInfo); continue; } PathSelectItem pathSelectItem = selectItem as PathSelectItem; if (pathSelectItem != null) { // $select=abc/.../xyz BuildSelectItem(pathSelectItem, currentLevelPropertiesInclude, structuralTypeInfo); continue; } WildcardSelectItem wildCardSelectItem = selectItem as WildcardSelectItem; if (wildCardSelectItem != null) { // $select=* MergeAllStructuralProperties(structuralTypeInfo.AllStructuralProperties, currentLevelPropertiesInclude); MergeSelectedNavigationProperties(structuralTypeInfo.AllNavigationProperties); SelectAllDynamicProperties = true; continue; } NamespaceQualifiedWildcardSelectItem wildCardActionSelection = selectItem as NamespaceQualifiedWildcardSelectItem; if (wildCardActionSelection != null) { // $select=NS.* AddNamespaceWildcardOperation(wildCardActionSelection, structuralTypeInfo.AllActions, structuralTypeInfo.AllFunctions); continue; } throw new ODataException(Error.Format(SRResources.SelectionTypeNotSupported, selectItem.GetType().Name)); } if (selectExpandClause.AllSelected) { MergeAllStructuralProperties(structuralTypeInfo.AllStructuralProperties, currentLevelPropertiesInclude); MergeSelectedNavigationProperties(structuralTypeInfo.AllNavigationProperties); MergeSelectedAction(structuralTypeInfo.AllActions); MergeSelectedFunction(structuralTypeInfo.AllFunctions); SelectAllDynamicProperties = true; } // to make sure the structural properties are in the same order defined in the type. if (structuralTypeInfo.AllStructuralProperties != null) { foreach (var structuralProperty in structuralTypeInfo.AllStructuralProperties) { SelectExpandIncludedProperty includeProperty; if (!currentLevelPropertiesInclude.TryGetValue(structuralProperty, out includeProperty)) { continue; } PathSelectItem pathSelectItem = includeProperty == null ? null : includeProperty.ToPathSelectItem(); AddStructuralProperty(structuralProperty, pathSelectItem); } } }