private QueryValue ApplySelectAndExpand(ODataUri uri, QueryValue value) { // Note: this is applied on all URIs, and it is left up to the FixupPropertiesForExpand helper method // to skip cases where it is not an entity type var expandedPaths = uri.ExpandSegments.Select(s => this.UriConverter.ConcatenateSegments(s)); value = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, expandedPaths)); var collection = value as QueryCollectionValue; // since we remove expand expression earlier, we need explictly examine it here and set correct IsSorted value for collections. if (collection != null) { var strategy = collection.Type.ElementType.EvaluationStrategy as ILinqToAstoriaQueryEvaluationStrategy; ExceptionUtilities.CheckObjectNotNull(strategy, "Cannot get astoria-specific evaluation strategy from collection value."); if (uri.ExpandSegments.Any() && !strategy.IsCollectionOrderPredictable) { value = QueryCollectionValue.Create(collection.Type.ElementType, collection.Elements, false); } } // if there are any select segments, then fixup the value if (uri.SelectSegments.Count > 0) { // post-process the result to omit properties that were not selected var selectedPaths = uri.SelectSegments.Select(s => this.UriConverter.ConcatenateSegments(s.Where(s2 => !(s2 is EntityTypeSegment)))); value = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, selectedPaths)); } return(value); }
/// <summary> /// Builds an instance of QueryCollectionValue from an entity set instance. /// </summary> /// <param name="entitySetInstance">The entity set instance.</param> /// <param name="elementType">The QueryEntityType of its element.</param> /// <param name="xmlBaseAnnotations">The xml base annotations from parent elements, if any</param> /// <returns>The converted QueryCollectionValue of entity set instance.</returns> private QueryCollectionValue BuildFromEntitySetInstance(EntitySetInstance entitySetInstance, QueryEntityType elementType, IEnumerable <XmlBaseAnnotation> xmlBaseAnnotations) { ExceptionUtilities.CheckArgumentNotNull(elementType, "elementType"); var entities = new List <QueryStructuralValue>(); foreach (var entityInstance in entitySetInstance) { var value = this.BuildFromEntityInstance(entityInstance, elementType, xmlBaseAnnotations.Concat(entityInstance.Annotations.OfType <XmlBaseAnnotation>())); entities.Add(value); } return(QueryCollectionValue.Create(elementType, entities.ToArray())); }
/// <summary> /// Adds an ordering expression for each key property /// </summary> /// <param name="queryCollectionValue">QueryCollection Value to sort</param> /// <returns>The result of adding the ordering expressions</returns> private QueryCollectionValue AddOrderingForPagingToQueryCollectionValue(QueryCollectionValue queryCollectionValue) { ExceptionUtilities.CheckArgumentNotNull(queryCollectionValue, "queryCollectionValue"); ExceptionUtilities.CheckObjectNotNull(this.expectedEntitySet, "No entity set expected"); var queryEntityType = (QueryEntityType)queryCollectionValue.Type.ElementType; var orderedElements = queryCollectionValue.Elements.OfType <QueryStructuralValue>(); // the product will sort key properties alphabetically foreach (var key in queryEntityType.EntityType.AllKeyProperties.OrderBy(p => p.Name)) { orderedElements = orderedElements.OrderBy(o => o.GetScalarValue(key.Name).Value); } return(QueryCollectionValue.Create(queryCollectionValue.Type.ElementType, orderedElements.ToArray())); }
/// <summary> /// Evaluates the specified expression. /// </summary> /// <param name="expression">The expression to evaluate.</param> /// <returns>Value of the expression.</returns> public override QueryValue Visit(LinqOrderByExpression expression) { var value = this.VisitCollectionElementPrimitiveOrComplexTypeError( expression, delegate { return(base.Visit(expression)); }); var collection = value as QueryCollectionValue; var strategy = collection.Type.ElementType.EvaluationStrategy as ILinqToAstoriaQueryEvaluationStrategy; ExceptionUtilities.CheckObjectNotNull(strategy, "Cannot get astoria-specific evaluation strategy from collection value."); if (strategy.IsCollectionOrderPredictable) { return(QueryCollectionValue.Create(collection.Type.ElementType, collection.Elements, true)); } return(value); }
/// <summary> /// Initializes query collection values. /// </summary> /// <param name="entitySetData">entity set data</param> /// <returns>initial query collection value</returns> protected virtual QueryCollectionValue BuildStubEntities(EntitySetData entitySetData) { string entitySetName = entitySetData.EntitySet.Name; var elements = new List <QueryStructuralValue>(); var setRowMap = new Dictionary <EntityDataKey, QueryStructuralValue>(); foreach (EntitySetDataRow row in entitySetData.Rows) { var queryType = this.GetQueryType(entitySetData.EntitySet.Name, row.EntityType); var instance = this.InitializeEntityValue(queryType); setRowMap[row.Key] = instance; elements.Add(instance); } this.rowInstances[entitySetName] = setRowMap; var rootElementType = this.GetQueryType(entitySetName, entitySetData.EntitySet.EntityType); return(QueryCollectionValue.Create(rootElementType, elements.ToArray())); }
/// <summary> /// Visits a LinqExpandExpression. /// </summary> /// <param name="expression">The expression.</param> /// <returns>Value of the expression</returns> public QueryValue Visit(LinqToAstoriaExpandExpression expression) { // expand is not handled here, instead it's handled in the trimming phase after // the whole expression has been evaluated var expanded = this.Evaluate(expression.Source); // if expanding a collection using sql strategy, we do not guarantee the order of top level set. var collection = expanded as QueryCollectionValue; if (collection != null) { var strategy = collection.Type.ElementType.EvaluationStrategy as ILinqToAstoriaQueryEvaluationStrategy; ExceptionUtilities.CheckObjectNotNull(strategy, "Cannot get astoria-specific evaluation strategy from collection value."); if (!strategy.IsCollectionOrderPredictable) { return(QueryCollectionValue.Create(collection.Type.ElementType, collection.Elements, false)); } } return(expanded); }
/// <summary> /// Builds an instance of QueryCollectionValue from an entity set instance. /// </summary> /// <param name="entitySetInstance">The entity set instance.</param> /// <param name="elementType">The QueryEntityType of its element.</param> /// <param name="xmlBaseAnnotations">The xml base annotations from parent elements, if any</param> /// <returns>The converted QueryCollectionValue of entity set instance.</returns> private QueryCollectionValue BuildFromEntitySetInstance(EntitySetInstance entitySetInstance, QueryEntityType elementType, IEnumerable<XmlBaseAnnotation> xmlBaseAnnotations) { ExceptionUtilities.CheckArgumentNotNull(elementType, "elementType"); var entities = new List<QueryValue>(); QueryValue value; foreach (var instance in entitySetInstance) { EntityInstance entity = instance as EntityInstance; if (entity != null) { value = this.BuildFromEntityInstance(entity, elementType, xmlBaseAnnotations.Concat(entity.Annotations.OfType<XmlBaseAnnotation>())); } else { value = this.BuildFromPrimitive((PrimitiveValue)instance, elementType); } entities.Add(value); } return QueryCollectionValue.Create(elementType, entities.ToArray()); }
/// <summary> /// Build a sub query data set from payload, but having enough information and correct order for evaluation. /// </summary> /// <param name="payload">The ODataPayloadElement converted from payload.</param> /// <returns>A sub query data set containing converted query value.</returns> private IQueryDataSet BuildQueryDataSet(ODataPayloadElement payload) { var dataSet = new QueryDataSet(); var xmlBaseAnnotations = payload.Annotations.OfType <XmlBaseAnnotation>(); var entityInstance = payload as EntityInstance; if (entityInstance != null) { // get QueryEntityType for entity instance. Notice that even we are expecting an anonymous type from projection, we still need to find out the 'original' entity type, since we need to build a query data set. var elementType = this.currentExpression.ExpressionType as QueryEntityType; if (elementType == null) { var collectionType = this.currentExpression.ExpressionType as QueryCollectionType; ExceptionUtilities.CheckObjectNotNull(collectionType, "Cannot cast expression type to QueryCollectionType."); elementType = collectionType.ElementType as QueryEntityType; if (elementType == null) { var anonymousType = collectionType.ElementType as QueryAnonymousStructuralType; ExceptionUtilities.CheckArgumentNotNull(anonymousType, "It must be an anonymous type if it is not an entity type."); // TODO: It may have problems if client side's entity types have diferent names with those on server side. // A solution is implementing another query expression visitor, removing all the $select expression, and using the type from expression.ExpressionType. elementType = this.GetQueryEntityTypeByFullTypeName(entityInstance.FullTypeName); } } // build query value and store it in query data set. var entity = this.BuildFromEntityInstance(entityInstance, elementType, xmlBaseAnnotations); var collection = QueryCollectionValue.Create(this.DefaultDataSet[elementType.EntitySet.Name].Type.ElementType, new List <QueryValue>() { entity }, true); dataSet.RootQueryData.Add(elementType.EntitySet.Name, collection); } else { var entitySetInstance = payload as EntitySetInstance; ExceptionUtilities.CheckObjectNotNull(entitySetInstance, "Payload was neither a feed nor entity. Type was: {0}", payload.ElementType); var collectionType = this.currentExpression.ExpressionType as QueryCollectionType; ExceptionUtilities.CheckObjectNotNull(collectionType, "Cannot cast expression type to QueryCollectionType."); var elementType = collectionType.ElementType as QueryEntityType; if (elementType == null) { var anonymousType = collectionType.ElementType as QueryAnonymousStructuralType; ExceptionUtilities.CheckArgumentNotNull(anonymousType, "It must be an anonymous type if it is not an entity type."); var title = entitySetInstance.Annotations.OfType <TitleAnnotation>().SingleOrDefault(); ExceptionUtilities.CheckObjectNotNull(title, "Cannot find title information from entity set instance."); ExceptionUtilities.CheckObjectNotNull(title.Value, "Cannot get title value from entity set instance."); QueryStructuralType rootDataType; this.QueryRepository.RootDataTypes.TryGetValue(title.Value, out rootDataType); if (rootDataType == null) { this.RootDataTypesDerivedTypes().TryGetValue(title.Value, out rootDataType); } ExceptionUtilities.CheckObjectNotNull(rootDataType, "Cannot find title value '{0}' in root datatypes", title.Value); elementType = rootDataType as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(elementType, "Cannot find element type for entity set '{0}'.", title.Value); } var collection = this.BuildFromEntitySetInstance(entitySetInstance, elementType, xmlBaseAnnotations); dataSet.RootQueryData.Add(elementType.EntitySet.Name, collection); } return(dataSet); }
private void SynchronizeNavigationProperty(QueryEntityType entitySetBaseType, QueryStructuralValue entity, SerializableNamedValue namedValue, IDictionary <EntityDataKey, QueryStructuralValue> existingEntityGraph) { var reference = namedValue.Value as SerializableEntity; if (reference != null) { QueryStructuralValue synchronized; if (reference.EntitySetName == entitySetBaseType.EntitySet.Name) { // for self references, we don't need to rebuild the set of existing entities synchronized = this.FindAndSynchronizeEntity(entitySetBaseType, reference, existingEntityGraph); } else { synchronized = this.FindAndSynchronizeEntity(reference); } entity.SetValue(namedValue.Name, synchronized); return; } var collection = namedValue.Value as IEnumerable <SerializableEntity>; if (collection != null) { var oldCollection = entity.GetCollectionValue(namedValue.Name); // clear out the old collection, if it exists if (entity.MemberNames.Contains(namedValue.Name)) { oldCollection.Elements.Clear(); } else { // by default we set IsSorted to true, enabling ordering verification. oldCollection = QueryCollectionValue.Create(oldCollection.Type.ElementType, new QueryValue[0], true); } entity.SetValue(namedValue.Name, oldCollection); // go through the new elements, synchronize them, and add them foreach (var related in collection) { QueryStructuralValue synchronized; if (related.EntitySetName == entitySetBaseType.EntitySet.Name) { // for self references, we don't need to rebuild the set of existing entities synchronized = this.FindAndSynchronizeEntity(entitySetBaseType, related, existingEntityGraph); } else { synchronized = this.FindAndSynchronizeEntity(related); } oldCollection.Elements.Add(synchronized); } return; } ExceptionUtilities.Assert(namedValue.Value == null, "Value of navigation property '{0}' was not a reference, a collection of references, or null. Value was '{1}'", namedValue.Name, namedValue.Value); entity.SetValue(namedValue.Name, entity.Type.Properties.Single(p => p.Name == namedValue.Name).PropertyType.NullValue); }
/// <summary> /// Initializes query collection values, setting default IsSorted value to true. Therefore we enable ordering verfication by default. /// </summary> /// <param name="entitySetData">entity set data</param> /// <returns>initial query collection value</returns> protected override QueryCollectionValue BuildStubEntities(EntitySetData entitySetData) { var collection = base.BuildStubEntities(entitySetData); return(QueryCollectionValue.Create(collection.Type.ElementType, collection.Elements, true)); }