Beispiel #1
0
 /// <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;
 }
Beispiel #2
0
        /// <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>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>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 <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>();
 }
Beispiel #11
0
        /// <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);
        }
Beispiel #12
0
        /// <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);
        }
Beispiel #13
0
        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));
        }
Beispiel #14
0
#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);
        }
Beispiel #15
0
        /// <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));
        }
Beispiel #16
0
        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;
        }