/// <summary> /// Verify that the property referred by the existing node and the new segment are both open properties or declared properties /// and if the existing node is an expand node, make sure that the target resource types are the same. /// </summary> /// <param name="existingNode">Existing node with the same property name.</param> /// <param name="property">ResourceProperty instance for the property refered by the new segment.</param> /// <param name="targetResourceType">TargetResourceType for the new segment.</param> /// <param name="expandNode">true if the existingNode is an expand node.</param> private static void VerifyPropertyMismatchAndExpandSelectMismatchScenario(ProjectionNode existingNode, ResourceProperty property, ResourceType targetResourceType, bool expandNode) { Debug.Assert( existingNode.TargetResourceType.IsAssignableFrom(targetResourceType) || targetResourceType.IsAssignableFrom(existingNode.TargetResourceType), "This method must be called if the existingNode and targetResourceType are in the same inheritance chain"); Debug.Assert(!expandNode || existingNode is ExpandedProjectionNode, "If expandNode is true, then the existingNode must be an ExpandedProjectionNode"); if (property != existingNode.Property) { // If the property are not the same - it means one of them must be null. // This is only possible if super type is open and the property resolves to an open property. Debug.Assert(property == null || existingNode.Property == null, "One of the properties must be null, since the types belong to the same inheritance chain, and cannot have different property instance from a given property name"); // Currently we do not support scenarios where one refers to the open property on the supertype // and declared property on the sub type. throw DataServiceException.CreateBadRequestError( Strings.RequestQueryProcessor_CannotSpecifyOpenPropertyAndDeclaredPropertyAtTheSameTime( existingNode.PropertyName, property == null ? targetResourceType.FullName : existingNode.TargetResourceType.FullName, property == null ? existingNode.TargetResourceType.FullName : targetResourceType.FullName)); } if (!ResourceType.CompareReferences(targetResourceType, existingNode.TargetResourceType) && expandNode) { // If expand and select are specified on the same property within a given type // hierarchy, currently we enforce that they must be specified on the same type throw DataServiceException.CreateBadRequestError( Strings.RequestQueryProcessor_SelectAndExpandCannotBeSpecifiedTogether(existingNode.PropertyName)); } }
/// <summary>Adds a new child node to this node.</summary> /// <param name="node">The child node to add.</param> private void AddNode(ProjectionNode node) { Debug.Assert(node != null, "node != null"); Debug.Assert( this.nodes.Count( n => n.PropertyName == node.PropertyName && (n.TargetResourceType.IsAssignableFrom(node.TargetResourceType) || node.TargetResourceType.IsAssignableFrom(n.TargetResourceType))) == 0, "make sure there is no node with the same property name in the inheritance hierarchy"); this.nodes.Add(node); ExpandedProjectionNode expandedNode = node as ExpandedProjectionNode; if (expandedNode != null) { this.hasExpandedPropertyOnDerivedType = this.hasExpandedPropertyOnDerivedType || (!ResourceType.CompareReferences(this.ResourceType, node.TargetResourceType)); } }
/// <summary>Removes duplicates from the tree caused by wildcards and sorts the projected properties.</summary> /// <param name="provider">underlying provider instance.</param> /// <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(DataServiceProviderWrapper provider) { // 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(provider); } } this.projectAllImmediateProperties = false; this.projectAllImmediateOperations = 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(provider); } } if (this.nodes.Count > 0) { // Sort the subsegments such that they have the same order as the properties // on the owning resource type. // build the list of existing resource types that this query touches List <ResourceType> resourceTypes = new List <ResourceType>(); resourceTypes.Add(this.ResourceType); // If we have one or more derived properties to expand or project, // we need to sort it based on the order in which the derived types // are return. List <ProjectionNode> derivedProjectionNodes = this.nodes.Where(n => !ResourceType.CompareReferences(n.TargetResourceType, this.ResourceType)).ToList(); if (derivedProjectionNodes.Count > 0) { foreach (ResourceType rt in provider.GetDerivedTypes(this.ResourceType)) { if (derivedProjectionNodes.FirstOrDefault(node => node.TargetResourceType == rt) != null) { resourceTypes.Add(rt); } } } #if DEBUG int count = this.nodes.Count; #endif this.nodes = ExpandedProjectionNode.SortNodes(this.nodes, resourceTypes); #if DEBUG Debug.Assert(this.nodes.Count == count, "We didn't sort all the properties."); #endif } }