/// <summary> /// Populate this collection with another collection of items /// </summary> /// <param name="items">The items to populate this collection with</param> private void InternalLoadCollection(IEnumerable <T> items) { Debug.Assert(items != null, "items != null"); // For SDP, we must execute the Query implicitly DataServiceQuery <T> query = items as DataServiceQuery <T>; if (query != null) { items = query.Execute() as QueryOperationResponse <T>; } foreach (T item in items) { // if this is too slow, consider hashing the set // or just use LoadProperties if (!this.Contains(item)) { this.Add(item); } } QueryOperationResponse <T> response = items as QueryOperationResponse <T>; if (response != null) { // this should never be throwing (since we've enumerated already)! // Note: Inner collection's nextPartLinkUri is set by the materializer this.continuation = response.GetContinuation(); } else { this.continuation = null; } }
/// <summary>Set the continuation for the following results for a collection.</summary> /// <param name="collection">The collection to set the links to</param> /// <param name="continuation">The continuation for the collection.</param> internal static void SetNextLinkForCollection(object collection, DataServiceQueryContinuation continuation) { Debug.Assert(collection != null, "collection != null"); // We do a convention call for setting Continuation. We'll invoke this // for all properties named 'Continuation' that is a DataServiceQueryContinuation // (assigning to a single one would make it inconsistent if reflection // order is changed). foreach (var property in collection.GetType().GetPublicProperties(true /*instanceOnly*/)) { if (property.Name != "Continuation" || !property.CanWrite) { continue; } if (typeof(DataServiceQueryContinuation).IsAssignableFrom(property.PropertyType)) { property.SetValue(collection, continuation, null); } } }
/// <summary> /// Returns the next link URI for the collection key /// </summary> /// <param name="key">The collection for which the Uri is returned, or null, if the top level link is to be returned</param> /// <returns>An Uri pointing to the next page for the collection</returns> internal virtual DataServiceQueryContinuation GetContinuation(IEnumerable key) { Debug.Assert(this.materializer != null, "Materializer is null!"); DataServiceQueryContinuation result; if (key == null) { if ((this.expectingPrimitiveValue && !this.moved) || (!this.expectingPrimitiveValue && !this.materializer.IsEndOfStream)) { // expectingSingleValue && !moved : haven't started parsing single value (single value should not have next link anyway) // !expectingSingleValue && !IsEndOfStream : collection type feed did not finish parsing yet throw new InvalidOperationException(Strings.MaterializeFromAtom_TopLevelLinkNotAvailable); } // we have already moved to the end of stream // are we singleton or just an entry? if (this.expectingPrimitiveValue || this.materializer.CurrentFeed == null) { result = null; } else { // DEVNOTE(pqian): The next link uri should never be edited by the client, and therefore it must be absolute result = DataServiceQueryContinuation.Create( this.materializer.CurrentFeed.NextPageLink, this.materializer.MaterializeEntryPlan); } } else { if (!this.materializer.NextLinkTable.TryGetValue(key, out result)) { // someone has asked for a collection that's "out of scope" or doesn't exist throw new ArgumentException(Strings.MaterializeFromAtom_CollectionKeyNotPresentInLinkTable); } } return(result); }
/// <summary> /// Populate this collection with another collection of items /// </summary> /// <param name="items">The items to populate this collection with</param> private void InternalLoadCollection(IEnumerable <T> items) { Debug.Assert(items != null, "items != null"); #if !ASTORIA_LIGHT && !PORTABLELIB // For SDP, we must execute the Query implicitly DataServiceQuery <T> query = items as DataServiceQuery <T>; if (query != null) { items = query.Execute() as QueryOperationResponse <T>; } #else Debug.Assert(!(items is DataServiceQuery), "SL Client using DSQ as items...should have been caught by ValidateIteratorParameter."); #endif foreach (T item in items) { // if this is too slow, consider hashing the set // or just use LoadProperties if (!this.Contains(item)) { this.Add(item); } } QueryOperationResponse <T> response = items as QueryOperationResponse <T>; if (response != null) { // this should never be throwing (since we've enumerated already)! // Note: Inner collection's nextPartLinkUri is set by the materializer this.continuation = response.GetContinuation(); } else { this.continuation = null; } }
private MaterializeAtom ReadPropertyFromAtom(ClientPropertyAnnotation property) { DataServiceContext context = (DataServiceContext)this.Source; bool merging = context.ApplyingChanges; try { context.ApplyingChanges = true; // store the results so that they can be there in the response body. Type elementType = property.IsEntityCollection ? property.EntityCollectionItemType : property.NullablePropertyType; IList results = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(elementType)); DataServiceQueryContinuation continuation = null; // elementType.ElementType has Nullable stripped away, use nestedType for materializer using (MaterializeAtom materializer = this.GetMaterializer(this.plan)) { Debug.Assert(materializer != null, "materializer != null -- otherwise GetMaterializer() returned null rather than empty"); #if ASTORIA_OPEN_OBJECT object openProperties = null; #endif // when SetLink to null, we cannot get materializer because have no-content response. if (materializer.IsNoContentResponse() && property.GetValue(entity) != null && context.MergeOption != MergeOption.AppendOnly && context.MergeOption != MergeOption.NoTracking) { property.SetValue(this.entity, null, propertyName, false); } else { foreach (object child in materializer) { if (property.IsEntityCollection) { results.Add(child); } else if (property.IsPrimitiveOrEnumOrComplexCollection) { Debug.Assert(property.PropertyType.IsAssignableFrom(child.GetType()), "Created instance for storing collection items has to be compatible with the actual one."); // Collection materialization rules requires to clear the collection if not null or set the property first and then add the collection items object collectionInstance = property.GetValue(this.entity); if (collectionInstance == null) { // type of child has been resolved as per rules for collections so it is the correct type to instantiate collectionInstance = Activator.CreateInstance(child.GetType()); // allowAdd is false - we need to assign instance as the new property value property.SetValue(this.entity, collectionInstance, this.propertyName, false /* allowAdd? */); } else { // Clear existing collection property.ClearBackingICollectionInstance(collectionInstance); } foreach (var collectionItem in (IEnumerable)child) { Debug.Assert(property.PrimitiveOrComplexCollectionItemType.IsAssignableFrom(collectionItem.GetType()), "Type of materialized collection items have to be compatible with the type of collection items in the actual collection property."); property.AddValueToBackingICollectionInstance(collectionInstance, collectionItem); } results.Add(collectionInstance); } else { #if ASTORIA_OPEN_OBJECT property.SetValue(this.entity, child, this.propertyName, ref openProperties, false); #else // it is either primitive type, complex type or 1..1 navigation property so we just allow setting the value but not adding. property.SetValue(this.entity, child, this.propertyName, false); results.Add(child); #endif } } } continuation = materializer.GetContinuation(null); } return(MaterializeAtom.CreateWrapper(context, results, continuation)); } finally { context.ApplyingChanges = merging; } }
/// <summary> /// Creates a wrapper for raw results /// </summary> /// <param name="context">Context of expression to analyze.</param> /// <param name="results">the results to wrap</param> /// <param name="continuation">The continuation for this query.</param> internal ResultsWrapper(DataServiceContext context, IEnumerable results, DataServiceQueryContinuation continuation) { this.context = context; this.results = results ?? Enumerable.Empty <object>(); this.continuation = continuation; }
/// <summary>Creates a materializer for partial result sets.</summary> /// <param name="context">Context of expression to analyze.</param> /// <param name="results">The current page of results</param> /// <param name="continuation">The continuation for the results.</param> /// <returns>A new materializer.</returns> internal static MaterializeAtom CreateWrapper(DataServiceContext context, IEnumerable results, DataServiceQueryContinuation continuation) { return(new ResultsWrapper(context, results, continuation)); }