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."); } }