/// <summary>constructor</summary> /// <param name="entity">entity</param> /// <param name="propertyName">name of collection or reference property to load</param> /// <param name="context">Originating context</param> /// <param name="request">Originating WebRequest</param> /// <param name="callback">user callback</param> /// <param name="state">user state</param> /// <param name="dataServiceRequest">request object.</param> /// <param name="plan">Projection plan for materialization; possibly null.</param> /// <param name="isContinuation">Whether this request is a continuation request.</param> internal LoadPropertyResult(object entity, string propertyName, DataServiceContext context, ODataRequestMessageWrapper request, AsyncCallback callback, object state, DataServiceRequest dataServiceRequest, ProjectionPlan plan, bool isContinuation) : base(context, Util.LoadPropertyMethodName, dataServiceRequest, request, new RequestInfo(context, isContinuation), callback, state) { this.entity = entity; this.propertyName = propertyName; this.plan = plan; }
/// <summary> /// Processes the result for successfull request and produces the actual result of the request. /// </summary> /// <typeparam name="TElement">Element type of the result.</typeparam> /// <param name="plan">The plan to use for the projection, if available in precompiled form.</param> /// <returns>A instance of QueryResponseResult created on top of of the request.</returns> internal QueryOperationResponse <TElement> ProcessResult <TElement>(ProjectionPlan plan) { Debug.Assert(this.responseInfo != null, "The request didn't complete yet, we don't have a response info for it."); MaterializeAtom materializeAtom = this.CreateMaterializer(plan, this.ServiceRequest.PayloadKind); return(this.GetResponse <TElement>(materializeAtom)); }
/// <summary>Create a request for a specific Uri</summary> /// <param name="requestUri">The URI for the request.</param> /// <param name="queryComponents">The query components for the request</param> /// <param name="plan">Projection plan to reuse (possibly null).</param> internal DataServiceRequest(Uri requestUri, QueryComponents queryComponents, ProjectionPlan plan) : this(requestUri) { Debug.Assert(requestUri != null, "requestUri != null"); Debug.Assert(queryComponents != null, "queryComponents != null"); this.queryComponents = queryComponents; this.plan = plan; }
/// <summary>Initializes a new <see cref="DataServiceQueryContinuation"/> instance.</summary> /// <param name="nextLinkUri">URI to next page of data.</param> /// <param name="plan">Projection plan for results of next page.</param> internal DataServiceQueryContinuation(Uri nextLinkUri, ProjectionPlan plan) { Debug.Assert(nextLinkUri != null, "nextLinkUri != null"); Debug.Assert(plan != null, "plan != null"); this.nextLinkUri = nextLinkUri; this.plan = plan; #if DEBUG this.deserializing = false; #endif }
/// <summary> /// Initializes a new instance of the <see cref="ODataReaderEntityMaterializer" /> class. /// </summary> /// <param name="odataMessageReader">The odata message reader.</param> /// <param name="reader">The reader.</param> /// <param name="materializerContext">The materializer context.</param> /// <param name="entityTrackingAdapter">The entity tracking adapter.</param> /// <param name="queryComponents">The query components.</param> /// <param name="expectedType">The expected type.</param> /// <param name="materializeEntryPlan">The materialize entry plan.</param> public ODataReaderEntityMaterializer( ODataMessageReader odataMessageReader, ODataReaderWrapper reader, IODataMaterializerContext materializerContext, EntityTrackingAdapter entityTrackingAdapter, QueryComponents queryComponents, Type expectedType, ProjectionPlan materializeEntryPlan) : base(materializerContext, entityTrackingAdapter, queryComponents, expectedType, materializeEntryPlan) { this.messageReader = odataMessageReader; this.feedEntryAdapter = new FeedAndEntryMaterializerAdapter(odataMessageReader, reader, materializerContext.Model, entityTrackingAdapter.MergeOption); }
/// <summary> /// Initializes a new instance of the <see cref="ODataEntriesEntityMaterializer" /> class. /// </summary> /// <param name="entries">The entries.</param> /// <param name="materializerContext">The materializer context.</param> /// <param name="entityTrackingAdapter">The entity tracking adapter.</param> /// <param name="queryComponents">The query components.</param> /// <param name="expectedType">The expected type.</param> /// <param name="materializeEntryPlan">The materialize entry plan.</param> /// <param name="format">The format.</param> public ODataEntriesEntityMaterializer( IEnumerable<ODataEntry> entries, IODataMaterializerContext materializerContext, EntityTrackingAdapter entityTrackingAdapter, QueryComponents queryComponents, Type expectedType, ProjectionPlan materializeEntryPlan, ODataFormat format) : base(materializerContext, entityTrackingAdapter, queryComponents, expectedType, materializeEntryPlan) { this.format = format; this.feedEntries = entries.GetEnumerator(); }
/// <summary>Creates a new <see cref="DataServiceQueryContinuation"/> instance.</summary> /// <param name="nextLinkUri">Link to next page of data (possibly null).</param> /// <param name="plan">Plan to materialize the data (only null if nextLinkUri is null).</param> /// <returns>A new continuation object; null if nextLinkUri is null.</returns> internal static DataServiceQueryContinuation Create(Uri nextLinkUri, ProjectionPlan plan) { Debug.Assert(plan != null || nextLinkUri == null, "plan != null || nextLinkUri == null"); if (nextLinkUri == null) { return(null); } var constructors = typeof(DataServiceQueryContinuation <>).MakeGenericType(plan.ProjectedType).GetInstanceConstructors(false /*isPublic*/); object result = Util.ConstructorInvoke(constructors.Single(), new object[] { nextLinkUri, plan }); return((DataServiceQueryContinuation)result); }
/// <summary> /// Initializes a new instance of the <see cref="ODataLoadNavigationPropertyMaterializer" /> class. /// </summary> /// <param name="odataMessageReader">The odata message reader.</param> /// <param name="reader">The reader.</param> /// <param name="materializerContext">The materializer context.</param> /// <param name="entityTrackingAdapter">The entity tracking adapter.</param> /// <param name="queryComponents">The query components.</param> /// <param name="expectedType">The expected type.</param> /// <param name="materializeEntryPlan">The materialize entry plan.</param> /// <param name="responseInfo">LoadProperty Response Info object.</param> public ODataLoadNavigationPropertyMaterializer( ODataMessageReader odataMessageReader, ODataReaderWrapper reader, IODataMaterializerContext materializerContext, EntityTrackingAdapter entityTrackingAdapter, QueryComponents queryComponents, Type expectedType, ProjectionPlan materializeEntryPlan, LoadPropertyResponseInfo responseInfo) : base(odataMessageReader, reader, materializerContext, entityTrackingAdapter, queryComponents, expectedType, materializeEntryPlan) { this.responseInfo = responseInfo; this.items = new List<object>(); }
/// <summary> /// Processes the result for successfull request and produces the actual result of the request. /// </summary> /// <typeparam name="TElement">Element type of the result.</typeparam> /// <param name="plan">The plan to use for the projection, if available in precompiled form.</param> /// <returns>A instance of QueryResponseResult created on top of of the request.</returns> internal QueryOperationResponse <TElement> ProcessResult <TElement>(ProjectionPlan plan) { Debug.Assert(this.responseInfo != null, "The request didn't complete yet, we don't have a response info for it."); MaterializeAtom materializeAtom = this.CreateMaterializer(plan, this.ServiceRequest.PayloadKind); var response = this.GetResponse <TElement>(materializeAtom); // When query feed, the instance annotation can be materialized only when enumerating the feed. // So we register this action which will be called when enumerating the feed. materializeAtom.SetInstanceAnnotations = (instanceAnnotations) => { if (!this.responseInfo.Context.InstanceAnnotations.ContainsKey(response)) { this.responseInfo.Context.InstanceAnnotations.Add(response, instanceAnnotations); } }; return(response); }
/// <summary> /// Create materializer on top of response stream /// </summary> /// <param name="plan">Precompiled projection plan (possibly null).</param> /// <returns>A materializer instance ready to deserialize ther result</returns> internal MaterializeAtom GetMaterializer(ProjectionPlan plan) { Debug.Assert(this.IsCompletedInternally, "request hasn't completed yet"); MaterializeAtom materializer; if (HttpStatusCode.NoContent != this.StatusCode) { Debug.Assert(this.responseInfo != null, "The request didn't complete yet, we don't have a response info for it."); materializer = this.CreateMaterializer(plan, ODataPayloadKind.Unsupported); } else { materializer = MaterializeAtom.EmptyResults; } return(materializer); }
internal static MaterializeAtom Materialize( ResponseInfo responseInfo, QueryComponents queryComponents, ProjectionPlan plan, string contentType, IODataResponseMessage message, ODataPayloadKind expectedPayloadKind) { Debug.Assert(null != queryComponents, "querycomponents"); Debug.Assert(null != message, "message"); // If there is no content (For e.g. /Customers(1)/BestFriend is null), we need to return empty results. if (message.StatusCode == (int)HttpStatusCode.NoContent || String.IsNullOrEmpty(contentType)) { return(MaterializeAtom.EmptyResults); } return(new MaterializeAtom(responseInfo, queryComponents, plan, message, expectedPayloadKind)); }
#pragma warning restore 649 #endif #endregion Private fields /// <summary> /// constructor /// </summary> /// <param name="responseInfo">originating context</param> /// <param name="queryComponents">Query components (projection, expected type)</param> /// <param name="plan">Projection plan (if compiled in an earlier query).</param> /// <param name="responseMessage">responseMessage</param> /// <param name="payloadKind">The kind of the payload to materialize.</param> internal MaterializeAtom( ResponseInfo responseInfo, QueryComponents queryComponents, ProjectionPlan plan, IODataResponseMessage responseMessage, ODataPayloadKind payloadKind) { Debug.Assert(queryComponents != null, "queryComponents != null"); this.responseInfo = responseInfo; this.elementType = queryComponents.LastSegmentType; this.expectingPrimitiveValue = PrimitiveType.IsKnownNullableType(elementType); Debug.Assert(responseMessage != null, "Response message is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?"); Type implementationType; Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType); this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind); }
/// <summary> /// Creates an instance of <see cref="MaterializeAtom"/> for the given plan. /// </summary> /// <param name="plan">The projection plan.</param> /// <param name="payloadKind">expected payload kind.</param> /// <returns>A new materializer instance</returns> private MaterializeAtom CreateMaterializer(ProjectionPlan plan, ODataPayloadKind payloadKind) { QueryComponents queryComponents = this.ServiceRequest.QueryComponents(this.responseInfo.Model); // In V2, in projection path, we did not check for assignability between the expected type and the type returned by the type resolver. if (plan != null || queryComponents.Projection != null) { this.RequestInfo.TypeResolver.IsProjectionRequest(); } var responseMessageWrapper = new HttpWebResponseMessage( new HeaderCollection(this.responseMessage), this.responseMessage.StatusCode, this.GetResponseStream); return(DataServiceRequest.Materialize( this.responseInfo, queryComponents, plan, this.ContentType, responseMessageWrapper, payloadKind)); }
public static ODataMaterializer CreateMaterializerForMessage( IODataResponseMessage responseMessage, ResponseInfo responseInfo, Type materializerType, QueryComponents queryComponents, ProjectionPlan plan, ODataPayloadKind payloadKind) { ODataMessageReader messageReader = CreateODataMessageReader(responseMessage, responseInfo, ref payloadKind); ODataMaterializer result; IEdmType edmType = null; try { ODataMaterializerContext materializerContext = new ODataMaterializerContext(responseInfo); // Since in V1/V2, astoria client allowed Execute<object> and depended on the typeresolver or the wire type name // to get the clr type to materialize. Hence if we see the materializer type as object, we should set the edmtype // to null, since there is no expected type. if (materializerType != typeof(System.Object)) { edmType = responseInfo.TypeResolver.ResolveExpectedTypeForReading(materializerType); } if (payloadKind == ODataPayloadKind.Entry || payloadKind == ODataPayloadKind.Feed) { // In V1/V2, we allowed System.Object type to be allowed to pass to ExecuteQuery. // Hence we need to explicitly check for System.Object to allow this if (edmType != null && edmType.TypeKind != EdmTypeKind.Entity) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(materializerType.FullName)); } ODataReaderWrapper reader = ODataReaderWrapper.Create(messageReader, payloadKind, edmType, responseInfo.ResponsePipeline); EntityTrackingAdapter entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context); LoadPropertyResponseInfo loadPropertyResponseInfo = responseInfo as LoadPropertyResponseInfo; if (loadPropertyResponseInfo != null) { result = new ODataLoadNavigationPropertyMaterializer( messageReader, reader, materializerContext, entityTrackingAdapter, queryComponents, materializerType, plan, loadPropertyResponseInfo); } else { result = new ODataReaderEntityMaterializer( messageReader, reader, materializerContext, entityTrackingAdapter, queryComponents, materializerType, plan); } } else { switch (payloadKind) { case ODataPayloadKind.Value: result = new ODataValueMaterializer(messageReader, materializerContext, materializerType, queryComponents.SingleResult); break; case ODataPayloadKind.Collection: result = new ODataCollectionMaterializer(messageReader, materializerContext, materializerType, queryComponents.SingleResult); break; case ODataPayloadKind.Property: case ODataPayloadKind.IndividualProperty: // Top level properties cannot be of entity type. if (edmType != null && edmType.TypeKind == EdmTypeKind.Entity) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidEntityType(materializerType.FullName)); } result = new ODataPropertyMaterializer(messageReader, materializerContext, materializerType, queryComponents.SingleResult); break; case ODataPayloadKind.EntityReferenceLinks: case ODataPayloadKind.EntityReferenceLink: result = new ODataLinksMaterializer(messageReader, materializerContext, materializerType, queryComponents.SingleResult); break; case ODataPayloadKind.Error: var odataError = messageReader.ReadError(); throw new ODataErrorException(odataError.Message, odataError); default: throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidResponsePayload(XmlConstants.DataWebNamespace)); } } return result; } catch (Exception ex) { if (CommonUtil.IsCatchableExceptionType(ex)) { // Dispose the message reader in all error scenarios. messageReader.Dispose(); } throw; } }
/// <summary>Creates a new <see cref="DataServiceQueryContinuation"/> instance.</summary> /// <param name="nextLinkUri">Link to next page of data (possibly null).</param> /// <param name="plan">Plan to materialize the data (only null if nextLinkUri is null).</param> /// <returns>A new continuation object; null if nextLinkUri is null.</returns> internal static DataServiceQueryContinuation Create(Uri nextLinkUri, ProjectionPlan plan) { Debug.Assert(plan != null || nextLinkUri == null, "plan != null || nextLinkUri == null"); if (nextLinkUri == null) { return null; } var constructors = typeof(DataServiceQueryContinuation<>).MakeGenericType(plan.ProjectedType).GetInstanceConstructors(false /*isPublic*/); object result = Util.ConstructorInvoke(constructors.Single(), new object[] { nextLinkUri, plan }); return (DataServiceQueryContinuation)result; }
/// <summary> /// Applies the values of the <paramref name="items"/> enumeration to the /// <paramref name="property"/> of the specified <paramref name="entry"/>. /// </summary> /// <param name="entry">Entry with collection to be modified.</param> /// <param name="property">Collection property on the entry.</param> /// <param name="items">Values to apply onto the collection.</param> /// <param name="nextLink">Next link for collection continuation.</param> /// <param name="continuationPlan">Projection plan for collection continuation.</param> /// <param name="isContinuation">Whether this is a continuation request.</param> internal void ApplyItemsToCollection( MaterializerEntry entry, ClientPropertyAnnotation property, IEnumerable items, Uri nextLink, ProjectionPlan continuationPlan, bool isContinuation) { Debug.Assert(entry.Entry != null || entry.ForLoadProperty, "ODataEntry should be non-null except for LoadProperty"); Debug.Assert(property != null, "property != null"); Debug.Assert(items != null, "items != null"); IEnumerable<object> itemsEnumerable = ODataEntityMaterializer.EnumerateAsElementType<object>(items); // Populate the collection property with items collection. object collection = this.PopulateCollectionProperty(entry, property, itemsEnumerable, nextLink, continuationPlan); // Get collection of all non-linked elements in collection and remove them except for the ones that were obtained from the response. if (this.EntityTrackingAdapter.MergeOption == MergeOption.OverwriteChanges || this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges) { var linkedItemsInCollection = this.EntityTrackingAdapter .EntityTracker .GetLinks(entry.ResolvedObject, property.PropertyName) .Select(l => new { l.Target, l.IsModified }); if (collection != null && !property.IsDictionary) { var nonLinkedNonReceivedItemsInCollection = ODataEntityMaterializer.EnumerateAsElementType<object>((IEnumerable)collection) .Except(linkedItemsInCollection.Select(i => i.Target)) .Except(itemsEnumerable).ToArray(); // Since no link exists, we just remove the item from the collection foreach (var item in nonLinkedNonReceivedItemsInCollection) { property.RemoveValue(collection, item); } } // When the first time a property or collection is being loaded, we remove all items other than the ones that we receive. if (!isContinuation) { IEnumerable<object> itemsToRemove; if (this.EntityTrackingAdapter.MergeOption == MergeOption.OverwriteChanges) { itemsToRemove = linkedItemsInCollection.Select(i => i.Target); } else { Debug.Assert( this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges, "this.EntityTrackingAdapter.MergeOption == MergeOption.PreserveChanges"); itemsToRemove = linkedItemsInCollection .Where(i => !i.IsModified) .Select(i => i.Target); } itemsToRemove = itemsToRemove.Except(itemsEnumerable); foreach (var item in itemsToRemove) { if (collection != null) { property.RemoveValue(collection, item); } this.EntityTrackingAdapter.MaterializationLog.RemovedLink(entry, property.PropertyName, item); } } } }
/// <summary>Records the fact that a rel='next' link was found for the specified <paramref name="collection"/>.</summary> /// <param name="collection">Collection to add link to.</param> /// <param name="link">Link (possibly null).</param> /// <param name="plan">Projection plan for the collection (null allowed only if link is null).</param> internal void FoundNextLinkForCollection(IEnumerable collection, Uri link, ProjectionPlan plan) { Debug.Assert(plan != null || link == null, "plan != null || link == null"); if (collection != null && !this.nextLinkTable.ContainsKey(collection)) { DataServiceQueryContinuation continuation = DataServiceQueryContinuation.Create(link, plan); this.nextLinkTable.Add(collection, continuation); Util.SetNextLinkForCollection(collection, continuation); } }
/// <summary>Creates a projection plan from the specified <paramref name="projection"/>.</summary> /// <param name="projection">Projection expression.</param> /// <param name="normalizerRewrites">Tracks rewrite-to-source rewrites introduced by expression normalizer.</param> /// <returns>A new <see cref="ProjectionPlan"/> instance.</returns> internal static ProjectionPlan CompilePlan(LambdaExpression projection, Dictionary<Expression, Expression> normalizerRewrites) { Debug.Assert(projection != null, "projection != null"); Debug.Assert(projection.Parameters.Count == 1, "projection.Parameters.Count == 1"); Debug.Assert( projection.Body.NodeType == ExpressionType.Constant || projection.Body.NodeType == ExpressionType.MemberInit || projection.Body.NodeType == ExpressionType.MemberAccess || projection.Body.NodeType == ExpressionType.Convert || projection.Body.NodeType == ExpressionType.ConvertChecked || projection.Body.NodeType == ExpressionType.New, "projection.Body.NodeType == Constant, MemberInit, MemberAccess, Convert(Checked) New"); ProjectionPlanCompiler rewriter = new ProjectionPlanCompiler(normalizerRewrites); #if TRACE_CLIENT_PROJECTIONS Trace.WriteLine("Projection: " + projection); #endif Expression plan = rewriter.Visit(projection); #if TRACE_CLIENT_PROJECTIONS Trace.WriteLine("Becomes: " + plan); #endif ProjectionPlan result = new ProjectionPlan(); result.Plan = (Func<object, object, Type, object>)((LambdaExpression)plan).Compile(); result.ProjectedType = projection.Body.Type; #if DEBUG result.SourceProjection = projection; result.TargetProjection = plan; #endif return result; }
/// <summary> /// Populates the collection property on the entry's resolved object with the given items enumerator. /// </summary> /// <param name="entry">Entry with collection to be modified.</param> /// <param name="property">Collection property on the entry.</param> /// <param name="items">Values to apply onto the collection.</param> /// <param name="nextLink">Next link for collection continuation.</param> /// <param name="continuationPlan">Projection plan for collection continuation.</param> /// <returns>Collection instance that was populated.</returns> private object PopulateCollectionProperty( MaterializerEntry entry, ClientPropertyAnnotation property, IEnumerable<object> items, Uri nextLink, ProjectionPlan continuationPlan) { Debug.Assert(entry.Entry != null || entry.ForLoadProperty, "ODataEntry should be non-null except for LoadProperty"); Debug.Assert(property != null, "property != null"); Debug.Assert(items != null, "items != null"); object collection = null; ClientEdmModel edmModel = this.MaterializerContext.Model; ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.EntityCollectionItemType)); if (entry.ShouldUpdateFromPayload) { collection = this.GetOrCreateCollectionProperty(entry.ResolvedObject, property, entry.ForLoadProperty); foreach (object item in items) { // Validate that item can be inserted into collection. ValidateCollectionElementTypeIsItemType(item.GetType(), collectionType.ElementType); property.SetValue(collection, item, property.PropertyName, true /* allowAdd? */); this.EntityTrackingAdapter.MaterializationLog.AddedLink(entry, property.PropertyName, item); } this.FoundNextLinkForCollection(collection as IEnumerable, nextLink, continuationPlan); } else { Debug.Assert(!entry.ForLoadProperty, "LoadProperty should always have ShouldUpateForPayload set to true."); foreach (object item in items) { // Validate that item can be inserted into collection. ValidateCollectionElementTypeIsItemType(item.GetType(), collectionType.ElementType); } this.FoundNextLinkForUnmodifiedCollection(property.GetValue(entry.ResolvedObject) as IEnumerable); } return collection; }