/// <summary> /// Fixes up the properties of the structural value based on the given selected paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="selectedPaths">The selected paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForSelect(QueryStructuralValue instance, IEnumerable <string> selectedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckCollectionNotEmpty(selectedPaths, "selectedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Select is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); bool wildCard = selectedPaths.Contains(Endpoints.SelectAll); var propertyNames = entityType.EntityType.AllProperties.Select(p => p.Name).Concat(entityType.Properties.Streams().Select(m => m.Name)); foreach (string propertyName in propertyNames) { if (!selectedPaths.Contains(Uri.EscapeDataString(propertyName)) && !wildCard) { // Primitive, bag and stream properties are either entirely present or entirely missing. // However, complex properties can be partially present if a mapped sub-property has a // null value. For this reason, we recursively hide the property and all sub-properties. HidePropertyRecursive(masked, propertyName); } } foreach (string propertyName in entityType.EntityType.AllNavigationProperties.Select(p => p.Name)) { string pathMarker = Uri.EscapeDataString(propertyName) + "/"; var subpaths = selectedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); if (selectedPaths.Contains(Uri.EscapeDataString(propertyName))) { continue; } else if (subpaths.Any()) { var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, subpaths)); masked.SetValue(propertyName, maskedValue); } else if (wildCard) { masked.SetValue(propertyName, value.Type.NullValue); } else { masked.HideMember(propertyName); } } return(masked); }
/// <summary> /// Fixes up the properties of the structural value based on the given expanded paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="expandedPaths">The expanded paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForExpand(QueryStructuralValue instance, IEnumerable <string> expandedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckArgumentNotNull(expandedPaths, "expandedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Expand is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); foreach (var navigation in entityType.EntityType.AllNavigationProperties) { string expandedPath; string propertyName = navigation.Name; var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); bool match = TryGetExpandedPath(expandedPaths, propertyName, entityType.EntityType, out expandedPath); string pathMarker = expandedPath + "/"; var subpaths = expandedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); // note that if there is match, we still need to fix up up any children, even if there are no subpaths if (subpaths.Any() || match) { // recurse var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, subpaths)); // handle page limit on expanded collections if (maskedValue.Type is QueryCollectionType) { var collection = maskedValue as QueryCollectionValue; ExceptionUtilities.CheckObjectNotNull(collection, "Value was a collection type, but not a collection value"); var relatedSet = entityType.EntitySet.GetRelatedEntitySet(navigation); var relatedSetPageSize = relatedSet.GetEffectivePageSize(); if (relatedSetPageSize.HasValue && this.applyPagingInExpands) { collection = this.AddOrderingForPagingToQueryCollectionValue(collection); maskedValue = collection.Take(relatedSetPageSize.Value); } } masked.SetValue(propertyName, maskedValue); } else if (!match) { // set any non-expanded navigation properties to null masked.SetValue(propertyName, value.Type.NullValue); } } return(masked); }
/// <summary> /// Compares structural object with the expected value. /// </summary> /// <param name="expected">Expected value.</param> /// <param name="actual">Actual value.</param> /// <param name="path">The path to the compared object (for debugging purposes).</param> /// <param name="shouldThrow">Should exception be thrown if error is encountered.</param> /// <returns> /// Result of the comparison, Success, Failure or Skipped. /// </returns> protected override ComparisonResult CompareStructural(QueryStructuralValue expected, object actual, string path, bool shouldThrow) { var hasDefaultStreamProperty = expected.Type.Properties.Any(p => p.Name == AstoriaQueryStreamType.DefaultStreamPropertyName); if (hasDefaultStreamProperty) { MaskedQueryStructuralValue newExpected = MaskedQueryStructuralValue.Create(expected); // need to remove the default stream property from the list of MemberNames for the expected value since the actual won't have that newExpected.HideMember(AstoriaQueryStreamType.DefaultStreamPropertyName); return(base.CompareStructural(newExpected, actual, path, shouldThrow)); } else { return(base.CompareStructural(expected, actual, path, shouldThrow)); } }
/// <summary> /// Recursively hides the properties, and any child properties. /// </summary> /// <param name="maskedQueryValue">The query value with properties to hide.</param> /// <param name="propertyName">The name of the property to hide.</param> private static void HidePropertyRecursive(MaskedQueryStructuralValue maskedQueryValue, string propertyName) { var propertyValue = maskedQueryValue.GetValue(propertyName) as QueryStructuralValue; if (propertyValue != null) { var maskedPropertyValue = propertyValue as MaskedQueryStructuralValue; if (maskedPropertyValue == null) { maskedPropertyValue = MaskedQueryStructuralValue.Create(propertyValue); } var subPropertyNames = maskedPropertyValue.AllMemberNames.ToList(); foreach (string subPropertyName in subPropertyNames) { HidePropertyRecursive(maskedPropertyValue, subPropertyName); } maskedQueryValue.SetValue(propertyName, maskedPropertyValue); } maskedQueryValue.HideMember(propertyName); }