/// <summary>Walks the subtree of this node and removes all nodes which were not marked projected.</summary> /// <remarks>Used to remove unnecessary expanded nodes.</remarks> internal void RemoveNonProjectedNodes() { for (int j = this.nodes.Count - 1; j >= 0; j--) { ExpandedProjectionNode expandedNode = this.nodes[j] as ExpandedProjectionNode; // Leave non-expanded properties there as they specify projections. if (expandedNode == null) { continue; } // If we are to project entire subtree, leave all expanded nodes in as well // otherwise remove the expanded nodes which are not marked as projected. if (!this.projectSubtree && !expandedNode.ProjectionFound) { // This removes the expandedNode from the tree (and all its children) this.nodes.RemoveAt(j); } else { expandedNode.RemoveNonProjectedNodes(); } } }
internal void MarkSubtreeAsProjected() { this.projectSubtree = true; this.projectAllImmediateProperties = false; foreach (ProjectionNode node in this.nodes) { ExpandedProjectionNode node2 = node as ExpandedProjectionNode; if (node2 != null) { node2.MarkSubtreeAsProjected(); } } }
internal ExpandedProjectionNode AddExpandedNode(ExpandSegment segment) { ExpandedProjectionNode node = (ExpandedProjectionNode) this.FindNode(segment.Name); if ((node != null) && (node.Property == segment.ExpandedProperty)) { if (segment.TargetResourceType.IsAssignableFrom(node.TargetResourceType)) { node.TargetResourceType = segment.TargetResourceType; } return node; } node = new ExpandedProjectionNode(segment.Name, segment.ExpandedProperty, segment.TargetResourceType, segment.Container, segment.OrderingInfo, segment.Filter, null, (segment.Container.PageSize != 0) ? new int?(segment.Container.PageSize) : null, (segment.MaxResultsExpected != 0x7fffffff) ? new int?(segment.MaxResultsExpected) : null); this.AddNode(node); return node; }
internal ExpandedProjectionNode AddExpandedNode(ExpandSegment segment) { ExpandedProjectionNode node = (ExpandedProjectionNode)this.FindNode(segment.Name); if ((node != null) && (node.Property == segment.ExpandedProperty)) { if (segment.TargetResourceType.IsAssignableFrom(node.TargetResourceType)) { node.TargetResourceType = segment.TargetResourceType; } return(node); } node = new ExpandedProjectionNode(segment.Name, segment.ExpandedProperty, segment.TargetResourceType, segment.Container, segment.OrderingInfo, segment.Filter, null, (segment.Container.PageSize != 0) ? new int?(segment.Container.PageSize) : null, (segment.MaxResultsExpected != 0x7fffffff) ? new int?(segment.MaxResultsExpected) : null); this.AddNode(node); return(node); }
internal void RemoveNonProjectedNodes() { for (int i = this.nodes.Count - 1; i >= 0; i--) { ExpandedProjectionNode node = this.nodes[i] as ExpandedProjectionNode; if (node != null) { if (!this.projectSubtree && !node.ProjectionFound) { this.nodes.RemoveAt(i); } else { node.RemoveNonProjectedNodes(); } } } }
private static bool ApplyPropertyToExistingNode(ProjectionNode existingNode, ResourceProperty property, System.Data.Services.Providers.ResourceType targetResourceType) { if (((property == null) || (existingNode.Property == null)) || (property == existingNode.Property)) { ExpandedProjectionNode node = existingNode as ExpandedProjectionNode; if (targetResourceType.IsAssignableFrom(existingNode.TargetResourceType)) { VerifyPropertyMismatchAndExpandSelectMismatchScenario(existingNode, property, targetResourceType, node != null); existingNode.TargetResourceType = targetResourceType; return(true); } if (existingNode.TargetResourceType.IsAssignableFrom(targetResourceType)) { VerifyPropertyMismatchAndExpandSelectMismatchScenario(existingNode, property, targetResourceType, node != null); return(true); } } return(false); }
private static ExpandedProjectionNode ApplyProjectionForProperty(ExpandedProjectionNode parentNode, string propertyName, ResourceProperty property, ResourceType targetResourceType, bool lastPathSegment) { if (property != null) { switch (property.TypeKind) { case ResourceTypeKind.ComplexType: if (!lastPathSegment) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.RequestQueryProcessor_ComplexPropertyAsInnerSelectSegment(targetResourceType.FullName, propertyName)); } break; case ResourceTypeKind.Primitive: if (!lastPathSegment) { if (property.IsOfKind(ResourcePropertyKind.Stream)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.RequestQueryProcessor_NamedStreamMustBeLastSegmentInSelect(propertyName)); } throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.RequestQueryProcessor_PrimitivePropertyUsedAsNavigationProperty(targetResourceType.FullName, propertyName)); } break; case ResourceTypeKind.Collection: if (!lastPathSegment) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.RequestQueryProcessor_CollectionPropertyAsInnerSelectSegment(targetResourceType.FullName, propertyName)); } break; } } ExpandedProjectionNode node = parentNode.AddProjectionNode(propertyName, property, targetResourceType, lastPathSegment); if (lastPathSegment && (node != null)) { node.ProjectionFound = true; node.MarkSubtreeAsProjected(); } return node; }
/// <summary>Recursive method which builds the $expand and $select paths for the specified node.</summary> /// <param name="parentPathSegments">List of path segments which lead up to this node. /// So for example if the specified node is Orders/OrderDetails the list will contains two strings /// "Orders" and "OrderDetails".</param> /// <param name="projectionPaths">The result to which the projection paths are appended as a comma separated list.</param> /// <param name="expansionPaths">The result to which the expansion paths are appended as a comma separated list.</param> /// <param name="expandedNode">The node to inspect.</param> /// <param name="foundProjections">Out parameter which is set to true if there were some explicit projections on the inspected node.</param> /// <param name="foundExpansions">Our parameter which is set to true if there were some expansions on the inspected node.</param> private void BuildProjectionAndExpansionPathsForNode( List<string> parentPathSegments, StringBuilder projectionPaths, StringBuilder expansionPaths, ExpandedProjectionNode expandedNode, out bool foundProjections, out bool foundExpansions) { foundProjections = false; foundExpansions = false; bool foundExpansionChild = false; bool foundProjectionChild = false; List<ExpandedProjectionNode> expandedChildrenNeededToBeProjected = new List<ExpandedProjectionNode>(); foreach (ProjectionNode childNode in expandedNode.Nodes) { ExpandedProjectionNode expandedChildNode = childNode as ExpandedProjectionNode; if (expandedChildNode == null) { // Explicitely project the property mentioned in this node AppendProjectionOrExpansionPath(projectionPaths, parentPathSegments, childNode.PropertyName); foundProjections = true; } else { foundExpansions = true; parentPathSegments.Add(expandedChildNode.PropertyName); this.BuildProjectionAndExpansionPathsForNode( parentPathSegments, projectionPaths, expansionPaths, expandedChildNode, out foundProjectionChild, out foundExpansionChild); parentPathSegments.RemoveAt(parentPathSegments.Count - 1); // Add projection paths for this node if all its properties should be projected if (expandedChildNode.ProjectAllProperties) { if (foundProjectionChild) { // There were some projections in our children, but this node requires all properties -> project * AppendProjectionOrExpansionPath(projectionPaths, parentPathSegments, childNode.PropertyName + "/*"); } else { // There were no projections underneath this node, so we need to "project" this node // we just don't know yet if we need to project this one explicitly or if some parent will do it for us implicitly. expandedChildrenNeededToBeProjected.Add(expandedChildNode); } } foundProjections |= foundProjectionChild; if (!foundExpansionChild) { // If there were no expansions in children, we need to add this node to expansion list AppendProjectionOrExpansionPath(expansionPaths, parentPathSegments, childNode.PropertyName); } } } if (!expandedNode.ProjectAllProperties || foundProjections) { // If we already projected some properties explicitely or this node does not want to project all properties // and we have some expanded children which were not projected yet // we need to project those explicitely (as the other projections disable the "include all" for this node // or we don't really want the "include all" anyway) foreach (ExpandedProjectionNode childToProject in expandedChildrenNeededToBeProjected) { AppendProjectionOrExpansionPath(projectionPaths, parentPathSegments, childToProject.PropertyName); // And since we're adding an explicit projection, mark us as using explicit projections foundProjections = true; } } }
/// <summary> /// Returns true if the subtree of expansions rooted in the specified <paramref name="expandedNode"/> /// contains either a filter or paging/maxresult constraint. /// </summary> /// <param name="expandedNode">The root of the expansions tree to inspect.</param> /// <returns>True if BasicExpandProvider should be used to process a query with this tree /// or false otherwise.</returns> private static bool ShouldUseBasicExpandProvider(ExpandedProjectionNode expandedNode) { foreach (ProjectionNode node in expandedNode.Nodes) { ExpandedProjectionNode childExpandedNode = node as ExpandedProjectionNode; if (childExpandedNode != null) { if (childExpandedNode.HasFilterOrMaxResults) { return true; } if (ShouldUseBasicExpandProvider(childExpandedNode)) { return true; } } } return false; }
internal void ApplyWildcardsAndSort(DataServiceProviderWrapper provider) { Func <ProjectionNode, bool> predicate = null; if (this.projectSubtree) { for (int i = this.nodes.Count - 1; i >= 0; i--) { ExpandedProjectionNode node = this.nodes[i] as ExpandedProjectionNode; if (node != null) { node.projectSubtree = true; node.ApplyWildcardsAndSort(provider); } else { this.nodes.RemoveAt(i); } } this.projectAllImmediateProperties = false; this.projectAllImmediateOperations = false; } else { for (int j = this.nodes.Count - 1; j >= 0; j--) { ExpandedProjectionNode node2 = this.nodes[j] as ExpandedProjectionNode; if (this.ProjectAllImmediateProperties && (node2 == null)) { this.nodes.RemoveAt(j); } else if (node2 != null) { node2.ApplyWildcardsAndSort(provider); } } if (this.nodes.Count > 0) { List <System.Data.Services.Providers.ResourceType> resourceTypesInMetadataOrder = new List <System.Data.Services.Providers.ResourceType> { this.ResourceType }; if (predicate == null) { predicate = n => !System.Data.Services.Providers.ResourceType.CompareReferences(n.TargetResourceType, this.ResourceType); } List <ProjectionNode> source = this.nodes.Where <ProjectionNode>(predicate).ToList <ProjectionNode>(); if (source.Count > 0) { using (IEnumerator <System.Data.Services.Providers.ResourceType> enumerator = provider.GetDerivedTypes(this.ResourceType).GetEnumerator()) { Func <ProjectionNode, bool> func = null; System.Data.Services.Providers.ResourceType rt; while (enumerator.MoveNext()) { rt = enumerator.Current; if (func == null) { func = node => node.TargetResourceType == rt; } if (source.FirstOrDefault <ProjectionNode>(func) != null) { resourceTypesInMetadataOrder.Add(rt); } } } } this.nodes = SortNodes(this.nodes, resourceTypesInMetadataOrder); } } }
/// <summary>Removes duplicates from the tree caused by wildcards and sorts the projected properties.</summary> /// <remarks> /// Examples /// $select=Orders, Orders/ID - get rid of the Orders/ID /// $select=Orders, Orders/* - get rid of the Orders/* /// $select=Orders/*, Orders/ID - get rid of the Orders/ID /// $select=Orders/*, Orders/OrderItems&$expand=Orders - get rid of the Orders/OrderItems (it's redundant to *) /// $select=Orders/*, Orders/OrderItems&$expand=Orders/OrderItems - leave as is, the Orders/OrderItems are expanded /// /// The sorting order is the same as the order in which the properties are enumerated on the owning type. /// This is to preserve the same order as if no projections occured. /// </remarks> internal void ApplyWildcardsAndSort() { // If this segment was marked to include entire subtree // simply remove all children which are not expanded // and propagate the information to all expanded children. if (this.projectSubtree) { for (int j = this.nodes.Count - 1; j >= 0; j--) { ExpandedProjectionNode expandedNode = this.nodes[j] as ExpandedProjectionNode; if (expandedNode != null) { expandedNode.projectSubtree = true; expandedNode.ApplyWildcardsAndSort(); } else { this.nodes.RemoveAt(j); } } this.projectAllImmediateProperties = false; return; } for (int j = this.nodes.Count - 1; j >= 0; j--) { ExpandedProjectionNode expandedNode = this.nodes[j] as ExpandedProjectionNode; // If this node was marked to include all immediate properties, // remove all children which are not expanded. // That means they are either simple properties or nav. properties which // are not going to be expanded anyway. if (this.ProjectAllImmediateProperties && expandedNode == null) { this.nodes.RemoveAt(j); } else if (expandedNode != null) { expandedNode.ApplyWildcardsAndSort(); } } if (this.nodes.Count > 0) { // Sort the subsegments such that they have the same order as the properties // on the owning resource type. ResourceType resourceType = this.ResourceType; List <ProjectionNode> existingNodes = this.nodes; this.nodes = new List <ProjectionNode>(existingNodes.Count); foreach (ResourceProperty property in resourceType.Properties) { Debug.Assert( existingNodes.Where(node => node.Property == property).Count() <= 1, "Can't have more than one projection segment for a given property."); ProjectionNode projectionNode = existingNodes.FirstOrDefault( node => node.Property == property); if (projectionNode != null) { this.nodes.Add(projectionNode); } } // And then append any open properties sorted alphabetically // We sort these since we don't want client to be able to influence // the server behavior unless abo----ely necessary. List <ProjectionNode> openPropertyProjectionNodes = existingNodes.Where(node => node.Property == null).ToList(); openPropertyProjectionNodes.Sort(new Comparison <ProjectionNode>((x, y) => { return(String.Compare(x.PropertyName, y.PropertyName, StringComparison.Ordinal)); })); this.nodes.AddRange(openPropertyProjectionNodes); Debug.Assert(this.nodes.Count == existingNodes.Count, "We didn't sort all the properties."); } }