/// <summary>
 /// Creates the materializer link with an entry.
 /// </summary>
 /// <param name="link">The link.</param>
 /// <param name="entry">The entry.</param>
 /// <returns>The materializer link.</returns>
 public static MaterializerNavigationLink CreateLink(ODataNavigationLink link, MaterializerEntry entry)
 {
     Debug.Assert(link.GetAnnotation<MaterializerNavigationLink>() == null, "there should be no MaterializerNavigationLink annotation on the entry link yet");
     MaterializerNavigationLink materializedNavigationLink = new MaterializerNavigationLink(link, entry);
     link.SetAnnotation<MaterializerNavigationLink>(materializedNavigationLink);
     return materializedNavigationLink;
 }
        /// <summary>Tries to resolve the object as one from the context (only if tracking is enabled).</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="expectedEntryType">Expected entry type for the specified <paramref name="entry"/>.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveFromContext(MaterializerEntry entry, Type expectedEntryType)
        {
            Debug.Assert(entry.IsTracking, "Should not be trying to resolve the entry from the context if entry.isTracking is false.");

            // We should either create a new instance or grab one from the context.
            if (this.MergeOption != MergeOption.NoTracking)
            {
                EntityStates state;
                entry.ResolvedObject = this.EntityTracker.TryGetEntity(entry.Id, out state);
                if (entry.ResolvedObject != null)
                {
                    if (!expectedEntryType.IsInstanceOfType(entry.ResolvedObject))
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_Current(expectedEntryType, entry.ResolvedObject.GetType()));
                    }

                    ClientEdmModel edmModel = this.Model;
                    entry.ActualType            = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(entry.ResolvedObject.GetType()));
                    entry.EntityHasBeenResolved = true;

                    // Note that deleted items will have their properties overwritten even
                    // if PreserveChanges is used as a merge option.
                    entry.ShouldUpdateFromPayload =
                        this.MergeOption == MergeOption.OverwriteChanges ||
                        (this.MergeOption == MergeOption.PreserveChanges && state == EntityStates.Unchanged) ||
                        (this.MergeOption == MergeOption.PreserveChanges && state == EntityStates.Deleted);

                    this.MaterializationLog.FoundExistingInstance(entry);

                    return(true);
                }
            }

            return(false);
        }
Beispiel #3
0
        /// <summary>
        /// Applies the values of a nested <paramref name="feed"/> to the collection
        /// <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="feed">Values to apply onto the collection.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        private void ApplyFeedToCollection(
            MaterializerEntry entry,
            ClientPropertyAnnotation property,
            ODataResourceSet feed,
            bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(feed != null, "feed != null");

            ClientEdmModel       edmModel       = this.MaterializerContext.Model;
            ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.ResourceSetItemType));

            IEnumerable <ODataResource> entries = MaterializerFeed.GetFeed(feed).Entries;

            foreach (ODataResource feedEntry in entries)
            {
                this.Materialize(MaterializerEntry.GetEntry(feedEntry), collectionType.ElementType, includeLinks);
            }

            ProjectionPlan continuationPlan = includeLinks ?
                                              ODataEntityMaterializer.CreatePlanForDirectMaterialization(property.ResourceSetItemType) :
                                              ODataEntityMaterializer.CreatePlanForShallowMaterialization(property.ResourceSetItemType);

            this.ApplyItemsToCollection(
                entry,
                property,
                entries.Select(e => MaterializerEntry.GetEntry(e).ResolvedObject),
                feed.NextPageLink,
                continuationPlan,
                false);
        }
 /// <summary>Projects a simple value from the specified <paramref name="path"/>.</summary>
 /// <param name="materializer">Materializer under which projection is taking place.</param>
 /// <param name="entry">Root entry for paths.</param>
 /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param>
 /// <param name="path">Path to pull value for.</param>
 /// <returns>The value for the specified <paramref name="path"/>.</returns>
 /// <remarks>
 /// This method will not instantiate entity types, except to satisfy requests
 /// for payload-driven feeds or leaf entities.
 /// </remarks>
 internal static object ProjectionValueForPath(object materializer, object entry, Type expectedType, object path)
 {
     Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())");
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
     return(((ODataEntityMaterializer)materializer).ProjectionValueForPath(MaterializerEntry.GetEntry((ODataEntry)entry), expectedType, (ProjectionPath)path));
 }
 /// <summary>Checks whether the entity on the specified <paramref name="path"/> is null.</summary>
 /// <param name="entry">Root entry for paths.</param>
 /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param>
 /// <param name="path">Path to pull value for.</param>
 /// <returns>Whether the specified <paramref name="path"/> is null.</returns>
 /// <remarks>
 /// This method will not instantiate entity types on the path.
 /// </remarks>
 internal static bool ProjectionCheckValueForPathIsNull(
     object entry,
     Type expectedType,
     object path)
 {
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
     return(ODataEntityMaterializer.ProjectionCheckValueForPathIsNull(MaterializerEntry.GetEntry((ODataEntry)entry), expectedType, (ProjectionPath)path));
 }
        /// <summary>
        /// Creates the materializer entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="format">The format the entry was read in.</param>
        /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param>
        /// <param name="model">The client model.</param>
        /// <returns>A new materializer entry.</returns>
        public static MaterializerEntry CreateEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model)
        {
            Debug.Assert(entry.GetAnnotation <MaterializerEntry>() == null, "MaterializerEntry has already been created.");

            MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model);

            entry.SetAnnotation <MaterializerEntry>(materializerEntry);

            return(materializerEntry);
        }
        /// <summary>
        /// Reads the remainder of an entry.
        /// </summary>
        /// <returns>An entry.</returns>
        private MaterializerEntry ReadEntryCore()
        {
            this.ExpectState(ODataReaderState.ResourceStart);

            ODataResource result = (ODataResource)this.reader.Item;

            MaterializerEntry entry;
            List <ODataNestedResourceInfo> navigationLinks = new List <ODataNestedResourceInfo>();

            if (result != null)
            {
                entry = MaterializerEntry.CreateEntry(
                    result,
                    this.readODataFormat,
                    this.mergeOption != MergeOption.NoTracking,
                    this.clientEdmModel);

                do
                {
                    this.AssertRead();

                    switch (this.reader.State)
                    {
                    case ODataReaderState.NestedResourceInfoStart:
                        // Cache the list of navigation links here but don't add them to the entry because all of the key properties may not be available yet.
                        navigationLinks.Add(this.ReadNestedResourceInfo());
                        break;

                    case ODataReaderState.ResourceEnd:
                        break;

                    default:
                        throw DSClient.Error.InternalError(InternalError.UnexpectedReadState);
                    }
                }while (this.reader.State != ODataReaderState.ResourceEnd);

                if (!entry.Entry.IsTransient)
                {
                    entry.UpdateEntityDescriptor();
                }
            }
            else
            {
                entry = MaterializerEntry.CreateEmpty();
                this.ReadAndExpectState(ODataReaderState.ResourceEnd);
            }

            // Add the navigation links here now that all of the property values have been read and are available to build the links.
            foreach (ODataNestedResourceInfo navigationLink in navigationLinks)
            {
                entry.AddNestedResourceInfo(navigationLink);
            }

            return(entry);
        }
 /// <summary>
 /// Tries to read an entry.
 /// </summary>
 /// <param name="entry">The entry.</param>
 /// <returns>true if a value was read, otherwise false</returns>
 private bool TryReadEntry(out MaterializerEntry entry)
 {
     if (this.TryStartReadFeedOrEntry())
     {
         this.ExpectState(ODataReaderState.ResourceStart);
         entry = this.ReadEntryCore();
         return(true);
     }
     else
     {
         entry = default(MaterializerEntry);
         return(false);
     }
 }
Beispiel #9
0
        /// <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, "ODataResource 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.ResourceSetItemType));

            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 ShouldUpdateForPayload 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);
        }
Beispiel #10
0
        /// <summary>"Resolved" the entity in the <paramref name="entry"/> by instantiating it.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="type">Type to create.</param>
        /// <remarks>
        /// After invocation, entry.ResolvedObject is exactly of type <paramref name="type"/>.
        /// </remarks>
        internal void ResolveByCreatingWithType(MaterializerEntry entry, Type type)
        {
            // TODO: CreateNewInstance needs to do all of these operations otherwise an inadvertent call to CreateNewInstance
            // will create a new entity instance that is not tracked in the context or materialization log. Will need to change this
            // prior to shipping if public
            Debug.Assert(
                entry.ResolvedObject == null,
                "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
            ClientEdmModel edmModel = this.MaterializerContext.Model;

            entry.ActualType              = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(type));
            entry.ResolvedObject          = this.CreateNewInstance(entry.ActualType.EdmTypeReference, type);
            entry.CreatedByMaterializer   = true;
            entry.ShouldUpdateFromPayload = true;
            entry.EntityHasBeenResolved   = true;
            this.EntityTrackingAdapter.MaterializationLog.CreatedInstance(entry);
        }
        /// <summary>Resolved or creates an instance on the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry on which to resolve or create an instance.</param>
        /// <param name="expectedEntryType">Expected type for the <paramref name="entry"/>.</param>
        /// <remarks>
        /// After invocation, the ResolvedObject value of the <paramref name="entry"/>
        /// will be assigned, along with the ActualType value.
        /// </remarks>
        /// <returns>True if an existing entity is found.</returns>
        internal virtual bool TryResolveExistingEntity(MaterializerEntry entry, Type expectedEntryType)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(expectedEntryType != null, "expectedEntryType != null");
            Debug.Assert(entry.EntityHasBeenResolved == false, "entry.EntityHasBeenResolved == false");

            // This will be the case when TargetInstance has been set.
            if (this.TryResolveAsTarget(entry))
            {
                return(true);
            }

            if (this.TryResolveAsExistingEntry(entry, expectedEntryType))
            {
                return(true);
            }

            return(false);
        }
Beispiel #12
0
        /// <summary>
        /// This method is for parsing CUD operation payloads which should contain
        /// 1 a single entry
        /// 2 An Error
        /// </summary>
        /// <param name="message">the message for the payload</param>
        /// <param name="responseInfo">The current ResponseInfo object</param>
        /// <param name="expectedType">The expected type</param>
        /// <returns>the MaterializerEntry that was read</returns>
        internal static MaterializerEntry ParseSingleEntityPayload(IODataResponseMessage message, ResponseInfo responseInfo, Type expectedType)
        {
            ODataPayloadKind messageType = ODataPayloadKind.Resource;

            using (ODataMessageReader messageReader = CreateODataMessageReader(message, responseInfo, ref messageType))
            {
                IEdmType           edmType = responseInfo.TypeResolver.ResolveExpectedTypeForReading(expectedType);
                ODataReaderWrapper reader  = ODataReaderWrapper.Create(messageReader, messageType, edmType, responseInfo.ResponsePipeline);

                FeedAndEntryMaterializerAdapter parser = new FeedAndEntryMaterializerAdapter(messageReader, reader, responseInfo.Model, responseInfo.MergeOption);

                ODataResource entry    = null;
                bool          readFeed = false;
                while (parser.Read())
                {
                    readFeed |= parser.CurrentFeed != null;
                    if (parser.CurrentEntry != null)
                    {
                        if (entry != null)
                        {
                            throw new InvalidOperationException(DSClient.Strings.AtomParser_SingleEntry_MultipleFound);
                        }

                        entry = parser.CurrentEntry;
                    }
                }

                if (entry == null)
                {
                    if (readFeed)
                    {
                        throw new InvalidOperationException(DSClient.Strings.AtomParser_SingleEntry_NoneFound);
                    }
                    else
                    {
                        throw new InvalidOperationException(DSClient.Strings.AtomParser_SingleEntry_ExpectedFeedOrEntry);
                    }
                }

                return(MaterializerEntry.GetEntry(entry));
            }
        }
        /// <summary>
        /// Tries to resolve the specified entry as an entry that has already been created in this materialization session or is already in the context.
        /// </summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="expectedEntryType">Expected type of the entry.</param>
        /// <returns>True if the entry was resolved, otherwise False.</returns>
        internal bool TryResolveAsExistingEntry(MaterializerEntry entry, Type expectedEntryType)
        {
            if (entry.Entry.IsTransient)
            {
                return(false);
            }

            // Resolution is based on the entry Id, so if we can't use that property, we don't even need to try to resolve it.
            if (entry.IsTracking)
            {
                // The resolver methods below will both need access to Id, so first ensure it's not null
                if (entry.Id == null)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MissingIdElement);
                }

                return(this.TryResolveAsCreated(entry) || this.TryResolveFromContext(entry, expectedEntryType));
            }

            return(false);
        }
        /// <summary>Tries to resolve the object from those created in this materialization session.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveAsCreated(MaterializerEntry entry)
        {
            Debug.Assert(entry.IsTracking, "Should not be trying to resolve the entry from the current materialization session if entry.isTracking is false.");

            MaterializerEntry existingEntry;

            if (!this.MaterializationLog.TryResolve(entry, out existingEntry))
            {
                return(false);
            }

            Debug.Assert(
                existingEntry.ResolvedObject != null,
                "existingEntry.ResolvedObject != null -- how did it get there otherwise?");
            entry.ActualType              = existingEntry.ActualType;
            entry.ResolvedObject          = existingEntry.ResolvedObject;
            entry.CreatedByMaterializer   = existingEntry.CreatedByMaterializer;
            entry.ShouldUpdateFromPayload = existingEntry.ShouldUpdateFromPayload;
            entry.EntityHasBeenResolved   = true;
            return(true);
        }
Beispiel #15
0
        /// <summary>
        /// Materializes the link properties if any with the url in the response payload
        /// </summary>
        /// <param name="actualType">Actual client type that is getting materialized.</param>
        /// <param name="entry">MaterializerEntry instance containing all the links that came in the response.</param>
        private static void ApplyLinkProperties(
            ClientTypeAnnotation actualType,
            MaterializerEntry entry)
        {
            Debug.Assert(actualType != null, "actualType != null");
            Debug.Assert(entry.Entry != null, "entry != null");

            if (entry.ShouldUpdateFromPayload)
            {
                foreach (var linkProperty in actualType.Properties().Where(p => p.PropertyType == typeof(DataServiceStreamLink)))
                {
                    string           propertyName = linkProperty.PropertyName;
                    StreamDescriptor streamDescriptor;
                    if (entry.EntityDescriptor.TryGetNamedStreamInfo(propertyName, out streamDescriptor))
                    {
                        // At this time we have materialized the stream link onto the stream descriptor object
                        // we'll always make sure the stream link is the same instance on the entity and the descriptor
                        linkProperty.SetValue(entry.ResolvedObject, streamDescriptor.StreamLink, propertyName, true /*allowAdd*/);
                    }
                }
            }
        }
Beispiel #16
0
        /// <summary>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="expectedEntryType">Expected type for the entry.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        internal void Materialize(MaterializerEntry entry, Type expectedEntryType, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(
                entry.ResolvedObject == null || entry.ResolvedObject == this.EntityTrackingAdapter.TargetInstance,
                "entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance -- otherwise getting called twice");
            Debug.Assert(expectedEntryType != null, "expectedType != null");

            // ResolvedObject will already be assigned when we have a TargetInstance, for example.
            if (!this.EntityTrackingAdapter.TryResolveExistingEntity(entry, expectedEntryType))
            {
                // If the type is a derived one this call will resolve to derived type, cannot put code in  ResolveByCreatingWithType as this is used by projection and in this case
                // the type cannot be changed or updated
                ClientTypeAnnotation actualType = this.MaterializerContext.ResolveTypeForMaterialization(expectedEntryType, entry.Entry.TypeName);

                this.ResolveByCreatingWithType(entry, actualType.ElementType);
            }

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise ResolveOrCreateInstnace didn't do its job");

            this.MaterializeResolvedEntry(entry, includeLinks);
        }
        /// <summary>
        /// Tries to read a feed or entry.
        /// </summary>
        /// <param name="lazy">if set to <c>true</c> [lazy].</param>
        /// <param name="feed">The feed.</param>
        /// <param name="entry">The entry.</param>
        /// <returns>true if a value was read, otherwise false</returns>
        private bool TryReadFeedOrEntry(bool lazy, out ODataResourceSet feed, out MaterializerEntry entry)
        {
            if (this.TryStartReadFeedOrEntry())
            {
                if (this.reader.State == ODataReaderState.ResourceStart)
                {
                    entry = this.ReadEntryCore();
                    feed  = null;
                }
                else
                {
                    entry = null;
                    feed  = this.ReadFeedCore(lazy);
                }
            }
            else
            {
                feed  = null;
                entry = null;
            }

            Debug.Assert(feed == null || entry == null, "feed == null || entry == null");
            return(feed != null || entry != null);
        }
        /// <summary>Tries to resolve the object as the target one in a POST refresh.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveAsTarget(MaterializerEntry entry)
        {
            if (entry.ResolvedObject == null)
            {
                return(false);
            }

            // The only case when the entity hasn't been resolved but
            // it has already been set is when the target instance
            // was set directly to refresh a POST.
            Debug.Assert(
                entry.ResolvedObject == this.TargetInstance,
                "entry.ResolvedObject == this.TargetInstance -- otherwise there we ResolveOrCreateInstance more than once on the same entry");
            Debug.Assert(
                this.MergeOption == MergeOption.OverwriteChanges || this.MergeOption == MergeOption.PreserveChanges,
                "MergeOption.OverwriteChanges and MergeOption.PreserveChanges are the only expected values during SaveChanges");
            ClientEdmModel edmModel = this.Model;

            entry.ActualType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(entry.ResolvedObject.GetType()));
            this.MaterializationLog.FoundTargetInstance(entry);
            entry.ShouldUpdateFromPayload = this.MergeOption != MergeOption.PreserveChanges;
            entry.EntityHasBeenResolved   = true;
            return(true);
        }
 /// <summary>Materializes an entry without including in-lined expanded links.</summary>
 /// <param name="materializer">Materializer under which materialization should take place.</param>
 /// <param name="entry">Entry with object to materialize.</param>
 /// <param name="expectedEntryType">Expected type for the entry.</param>
 /// <returns>The materialized instance.</returns>
 internal static object ShallowMaterializePlan(object materializer, object entry, Type expectedEntryType)
 {
     Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())");
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     return(ODataEntityMaterializer.ShallowMaterializePlan((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataEntry)entry), expectedEntryType));
 }
 /// <summary>Initializes a projection-driven entry (with a specific type and specific properties).</summary>
 /// <param name="materializer">Materializer under which projection is taking place.</param>
 /// <param name="entry">Root entry for paths.</param>
 /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param>
 /// <param name="resultType">Expected result type.</param>
 /// <param name="properties">Properties to materialize.</param>
 /// <param name="propertyValues">Functions to get values for functions.</param>
 /// <returns>The initialized entry.</returns>
 internal static object ProjectionInitializeEntity(
     object materializer,
     object entry,
     Type expectedType,
     Type resultType,
     string[] properties,
     Func <object, object, Type, object>[] propertyValues)
 {
     Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())");
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     return(ODataEntityMaterializer.ProjectionInitializeEntity((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataEntry)entry), expectedType, resultType, properties, propertyValues));
 }
        /// <summary>Resolved or creates an instance on the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry on which to resolve or create an instance.</param>
        /// <param name="expectedEntryType">Expected type for the <paramref name="entry"/>.</param>
        /// <remarks>
        /// After invocation, the ResolvedObject value of the <paramref name="entry"/>
        /// will be assigned, along with the ActualType value.
        /// </remarks>
        /// <returns>True if an existing entity is found.</returns>
        internal virtual bool TryResolveExistingEntity(MaterializerEntry entry, Type expectedEntryType)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(expectedEntryType != null, "expectedEntryType != null");
            Debug.Assert(entry.EntityHasBeenResolved == false, "entry.EntityHasBeenResolved == false");

            // This will be the case when TargetInstance has been set.
            if (this.TryResolveAsTarget(entry))
            {
                return true;
            }

            if (this.TryResolveAsExistingEntry(entry, expectedEntryType))
            {
                return true;
            }

            return false;
        }
 /// <summary>
 /// Invoke this method to notify the log that an existing
 /// instance was found while resolving an object.
 /// </summary>
 /// <param name="entry">Entry for instance.</param>
 internal void FoundExistingInstance(MaterializerEntry entry)
 {
     Debug.Assert(entry.Entry != null, "entry != null");
     Debug.Assert(IsEntity(entry), "Existing entries should be entity");
     this.identityStack[entry.Id] = entry.Entry;
 }
Beispiel #23
0
        /// <summary>
        /// Creates the materializer link with an entry.
        /// </summary>
        /// <param name="link">The link.</param>
        /// <param name="entry">The entry.</param>
        /// <returns>The materializer link.</returns>
        public static MaterializerNavigationLink CreateLink(ODataNestedResourceInfo link, MaterializerEntry entry)
        {
            Debug.Assert(link.GetAnnotation <MaterializerNavigationLink>() == null, "there should be no MaterializerNestedResourceInfo annotation on the entry link yet");
            MaterializerNavigationLink materializedNestedResourceInfo = new MaterializerNavigationLink(link, entry);

            link.SetAnnotation <MaterializerNavigationLink>(materializedNestedResourceInfo);
            return(materializedNestedResourceInfo);
        }
        /// <summary>
        /// Invoke this method to notify the log that a new link was
        /// added to a collection.
        /// </summary>
        /// <param name="source">
        /// Instance with the collection to which <paramref name="target"/> 
        /// was added.
        /// </param>
        /// <param name="propertyName">Property name for collection.</param>
        /// <param name="target">Object which was added.</param>
        internal void AddedLink(MaterializerEntry source, string propertyName, object target)
        {
            Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty");
            Debug.Assert(propertyName != null, "propertyName != null");

            if (!this.Tracking)
            {
                return;
            }

            if (IsEntity(source) && IsEntity(target, this.clientEdmModel))
            {
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added);
                this.links.Add(item);
            }
        }
Beispiel #25
0
        /// <summary>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="includeLinks">Whether links that are expanded for navigation property should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");

            ClientTypeAnnotation actualType = entry.ActualType;

            // While materializing entities, we need to make sure the payload that came in the wire is also an entity.
            // Otherwise we need to fail.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (!actualType.IsStructuredType)
            {
                throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(actualType.ElementTypeName));
            }

            // Note that even if ShouldUpdateFromPayload is false, we will still be creating
            // nested instances (but not their links), so they show up in the data context
            // entries. This keeps this code compatible with V1 behavior.
            this.MaterializeDataValues(actualType, entry.Properties, this.MaterializerContext.UndeclaredPropertyBehavior);

            if (entry.NestedResourceInfos?.Any() == true)
            {
                foreach (ODataNestedResourceInfo link in entry.NestedResourceInfos)
                {
                    var prop = actualType.GetProperty(link.Name, UndeclaredPropertyBehavior.Support);
                    if (prop != null)
                    {
                        ValidatePropertyMatch(prop, link, this.MaterializerContext.Model, true /*performEntityCheck*/);
                    }
                }

                foreach (ODataNestedResourceInfo link in entry.NestedResourceInfos)
                {
                    MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

                    if (linkState == null)
                    {
                        continue;
                    }

                    var prop = actualType.GetProperty(link.Name, this.MaterializerContext.UndeclaredPropertyBehavior);

                    if (prop == null)
                    {
                        if (entry.ShouldUpdateFromPayload)
                        {
                            this.MaterializeDynamicProperty(entry, link);
                        }
                        continue;
                    }

                    // includeLinks is for Navigation property, so we should handle complex property when includeLinks equals false;
                    if (!includeLinks && (prop.IsEntityCollection || prop.EntityCollectionItemType != null))
                    {
                        continue;
                    }

                    if (linkState.Feed != null)
                    {
                        this.ApplyFeedToCollection(entry, prop, linkState.Feed, includeLinks);
                    }
                    else if (linkState.Entry != null)
                    {
                        MaterializerEntry linkEntry = linkState.Entry;

                        if (linkEntry.Entry != null)
                        {
                            this.Materialize(linkEntry, prop.PropertyType, includeLinks);
                        }

                        if (entry.ShouldUpdateFromPayload)
                        {
                            prop.SetValue(entry.ResolvedObject, linkEntry.ResolvedObject, link.Name, true /* allowAdd? */);

                            if (!this.MaterializerContext.Context.DisableInstanceAnnotationMaterialization && linkEntry.ShouldUpdateFromPayload)
                            {
                                // Apply instance annotation for navigation property
                                this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(
                                    prop.PropertyName, linkEntry.Entry, entry.ActualType.ElementType, entry.ResolvedObject);

                                // Apply instance annotation for entity of the navigation property
                                this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(linkEntry.Entry, linkEntry.ResolvedObject);
                            }

                            this.EntityTrackingAdapter.MaterializationLog.SetLink(entry, prop.PropertyName, linkEntry.ResolvedObject);
                        }
                    }
                }
            }

            foreach (var e in entry.Properties)
            {
                if (e.Value is ODataStreamReferenceValue)
                {
                    continue;
                }

                var prop = actualType.GetProperty(e.Name, this.MaterializerContext.UndeclaredPropertyBehavior);
                if (prop == null)
                {
                    if (entry.ShouldUpdateFromPayload)
                    {
                        this.MaterializeDynamicProperty(e, entry.ResolvedObject);
                    }
                    continue;
                }

                if (entry.ShouldUpdateFromPayload)
                {
                    ValidatePropertyMatch(prop, e, this.MaterializerContext.Model, true /*performEntityCheck*/);

                    this.ApplyDataValue(actualType, e, entry.ResolvedObject);
                }
            }

            // apply link values if present
            ApplyLinkProperties(actualType, entry);

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");

            BaseEntityType entity = entry.ResolvedObject as BaseEntityType;

            if (entity != null)
            {
                entity.Context = this.EntityTrackingAdapter.Context;
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }
        /// <summary>
        /// Fires the end entry events.
        /// </summary>
        /// <param name="entry">The entry.</param>
        internal void FireEndEntryEvents(MaterializerEntry entry)
        {
            Debug.Assert(entry != null, "entry!=null");

            if (this.HasReadingEntityHandlers)
            {
                this.ExecuteEntityMaterializedActions(entry.Entry, entry.ResolvedObject);
            }
        }
Beispiel #27
0
            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="descriptor">descriptor whose response is getting processed.</param>
            /// <param name="headers">headers</param>
            /// <param name="statusCode">status code</param>
            /// <param name="responseVersion">Parsed response OData-Version header.</param>
            /// <param name="entry">atom entry, if there is a non-error response payload.</param>
            /// <param name="exception">exception, if the request threw an exception.</param>
            internal CachedResponse(Descriptor descriptor, HeaderCollection headers, HttpStatusCode statusCode, Version responseVersion, MaterializerEntry entry, Exception exception)
            {
                Debug.Assert(descriptor != null, "descriptor != null");
                Debug.Assert(headers != null, "headers != null");
                Debug.Assert(entry == null || (exception == null && descriptor.DescriptorKind == DescriptorKind.Entity), "if entry is specified, exception cannot be specified and entry must be a resource, since we expect responses only for entities");

                this.Descriptor = descriptor;
                this.MaterializerEntry = entry;
                this.Exception = exception;
                this.Headers = headers;
                this.StatusCode = statusCode;
                this.Version = responseVersion;
            }
Beispiel #28
0
        /// <summary>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");

            ClientTypeAnnotation actualType = entry.ActualType;

            // While materializing entities, we need to make sure the payload that came in the wire is also an entity.
            // Otherwise we need to fail.
            if (!actualType.IsEntityType)
            {
                throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(actualType.ElementTypeName));
            }

            // Note that even if ShouldUpdateFromPayload is false, we will still be creating
            // nested instances (but not their links), so they show up in the data context
            // entries. This keeps this code compatible with V1 behavior.
            this.MaterializeDataValues(actualType, entry.Properties, this.MaterializerContext.IgnoreMissingProperties);

            if (entry.NavigationLinks != null)
            {
                foreach (ODataNavigationLink link in entry.NavigationLinks)
                {
                    var prop = actualType.GetProperty(link.Name, true);
                    if (prop != null)
                    {
                        ValidatePropertyMatch(prop, link, this.MaterializerContext.Model, true /*performEntityCheck*/);
                    }
                }

                if (includeLinks)
                {
                    foreach (ODataNavigationLink link in entry.NavigationLinks)
                    {
                        MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

                        // Ignore...
                        if (linkState == null)
                        {
                            continue;
                        }

                        var prop = actualType.GetProperty(link.Name, this.MaterializerContext.IgnoreMissingProperties);

                        if (prop == null)
                        {
                            continue;
                        }

                        if (linkState.Feed != null)
                        {
                            this.ApplyFeedToCollection(entry, prop, linkState.Feed, includeLinks);
                        }
                        else if (linkState.Entry != null)
                        {
                            MaterializerEntry linkEntry = linkState.Entry;

                            if (linkEntry.Entry != null)
                            {
                                Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
                                this.Materialize(linkEntry, prop.PropertyType, includeLinks);
                            }

                            if (entry.ShouldUpdateFromPayload)
                            {
                                prop.SetValue(entry.ResolvedObject, linkEntry.ResolvedObject, link.Name, true /* allowAdd? */);
                                this.EntityTrackingAdapter.MaterializationLog.SetLink(entry, prop.PropertyName, linkEntry.ResolvedObject);
                            }
                        }
                    }
                }
            }

            foreach (var e in entry.Properties)
            {
                if (e.Value is ODataStreamReferenceValue)
                {
                    continue;
                }

                var prop = actualType.GetProperty(e.Name, this.MaterializerContext.IgnoreMissingProperties);
                if (prop == null)
                {
                    continue;
                }

                if (entry.ShouldUpdateFromPayload)
                {
                    ValidatePropertyMatch(prop, e, this.MaterializerContext.Model, true /*performEntityCheck*/);

                    this.ApplyDataValue(actualType, e, entry.ResolvedObject);
                }
            }

            // apply link values if present
            ApplyLinkProperties(actualType, entry);

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");

            BaseEntityType entity = entry.ResolvedObject as BaseEntityType;

            if (entity != null)
            {
                entity.Context = this.EntityTrackingAdapter.Context;
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }
        /// <summary>
        /// Implementation of Read/>.
        /// </summary>
        /// <returns>
        /// Return value of Read/>
        /// </returns>
        protected override bool ReadImplementation()
        {
            // Eagerly materialize the entire collection of objects into the items cache in LoadProperty scenario.
            if (this.iteration == 0)
            {
                while (base.ReadImplementation())
                {
                    this.items.Add(this.currentValue);
                }

                ClientPropertyAnnotation property         = this.responseInfo.Property;
                EntityDescriptor         entityDescriptor = this.responseInfo.EntityDescriptor;
                object entity = entityDescriptor.Entity;

                MaterializerEntry entry = MaterializerEntry.CreateEntryForLoadProperty(
                    entityDescriptor,
                    this.Format,
                    this.responseInfo.MergeOption != MergeOption.NoTracking);

                entry.ActualType = this.responseInfo.Model.GetClientTypeAnnotation(this.responseInfo.Model.GetOrCreateEdmType(entity.GetType()));

                if (property.IsEntityCollection)
                {
                    this.EntryValueMaterializationPolicy.ApplyItemsToCollection(
                        entry,
                        property,
                        this.items,
                        this.CurrentFeed != null ? this.CurrentFeed.NextPageLink : null,
                        this.MaterializeEntryPlan,
                        this.responseInfo.IsContinuation);
                }
                else
                {
                    Debug.Assert(this.items.Count <= 1, "Expecting 0 or 1 element.");
                    object target = this.items.Count > 0 ? this.items[0] : null;

                    Debug.Assert(property.EdmProperty.Type.TypeKind() == EdmTypeKind.Entity, "Must be entity typed property if not an entity collection property.");
                    this.EntityTrackingAdapter.MaterializationLog.SetLink(entry, property.PropertyName, target);

                    // Singleton entity property
                    property.SetValue(entity, target, property.PropertyName, false);
                }

                // Apply the materialization log.
                this.ApplyLogToContext();

                // Clear the log after applying it.
                this.ClearLog();
            }

            // Read object from the already loaded items cache.
            if (this.items.Count > this.iteration)
            {
                this.currentValue = this.items[this.iteration];
                this.iteration++;
                return(true);
            }
            else
            {
                this.currentValue = null;
                return(false);
            }
        }
        /// <summary>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");

            ClientTypeAnnotation actualType = entry.ActualType;

            // While materializing entities, we need to make sure the payload that came in the wire is also an entity.
            // Otherwise we need to fail.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (!actualType.IsEntityType)
            {
                throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(actualType.ElementTypeName));
            }

            // Note that even if ShouldUpdateFromPayload is false, we will still be creating
            // nested instances (but not their links), so they show up in the data context
            // entries. This keeps this code compatible with V1 behavior.
            this.MaterializeDataValues(actualType, entry.Properties, this.MaterializerContext.IgnoreMissingProperties);

            if (entry.NavigationLinks != null)
            {
                foreach (ODataNavigationLink link in entry.NavigationLinks)
                {
                    var prop = actualType.GetProperty(link.Name, true);
                    if (prop != null)
                    {
                        ValidatePropertyMatch(prop, link, this.MaterializerContext.Model, true /*performEntityCheck*/);
                    }
                }

                if (includeLinks)
                {
                    foreach (ODataNavigationLink link in entry.NavigationLinks)
                    {
                        MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

                        // Ignore...
                        if (linkState == null)
                        {
                            continue;
                        }

                        var prop = actualType.GetProperty(link.Name, this.MaterializerContext.IgnoreMissingProperties);

                        if (prop == null)
                        {
                            continue;
                        }

                        if (linkState.Feed != null)
                        {
                            this.ApplyFeedToCollection(entry, prop, linkState.Feed, includeLinks);
                        }
                        else if (linkState.Entry != null)
                        {
                            MaterializerEntry linkEntry = linkState.Entry;

                            if (linkEntry.Entry != null)
                            {
                                Debug.Assert(includeLinks, "includeLinks -- otherwise we shouldn't be materializing this entry");
                                this.Materialize(linkEntry, prop.PropertyType, includeLinks);
                            }

                            if (entry.ShouldUpdateFromPayload)
                            {
                                prop.SetValue(entry.ResolvedObject, linkEntry.ResolvedObject, link.Name, true /* allowAdd? */);

                                if (!this.MaterializerContext.Context.DisableInstanceAnnotationMaterialization && linkEntry.ShouldUpdateFromPayload)
                                {
                                    // Apply instance annotation for navigation property
                                    this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(
                                        prop.PropertyName, linkEntry.Entry, entry.ActualType.ElementType, entry.ResolvedObject);

                                    // Apply instance annotation for entity of the navigation property
                                    this.InstanceAnnotationMaterializationPolicy.SetInstanceAnnotations(linkEntry.Entry, linkEntry.ResolvedObject);
                                }

                                this.EntityTrackingAdapter.MaterializationLog.SetLink(entry, prop.PropertyName, linkEntry.ResolvedObject);
                            }
                        }
                    }
                }
            }

            foreach (var e in entry.Properties)
            {
                if (e.Value is ODataStreamReferenceValue)
                {
                    continue;
                }

                var prop = actualType.GetProperty(e.Name, this.MaterializerContext.IgnoreMissingProperties);
                if (prop == null)
                {
                    continue;
                }

                if (entry.ShouldUpdateFromPayload)
                {
                    ValidatePropertyMatch(prop, e, this.MaterializerContext.Model, true /*performEntityCheck*/);

                    this.ApplyDataValue(actualType, e, entry.ResolvedObject);
                }
            }

            // apply link values if present
            ApplyLinkProperties(actualType, entry);

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise we didn't do any useful work");

            BaseEntityType entity = entry.ResolvedObject as BaseEntityType;
            if (entity != null)
            {
                entity.Context = this.EntityTrackingAdapter.Context;
            }

            this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
        }
        /// <summary>
        /// Applies the values of a nested <paramref name="feed"/> to the collection
        /// <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="feed">Values to apply onto the collection.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        private void ApplyFeedToCollection(
            MaterializerEntry entry,
            ClientPropertyAnnotation property,
            ODataFeed feed,
            bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(property != null, "property != null");
            Debug.Assert(feed != null, "feed != null");

            ClientEdmModel edmModel = this.MaterializerContext.Model;
            ClientTypeAnnotation collectionType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(property.EntityCollectionItemType));

            IEnumerable<ODataEntry> entries = MaterializerFeed.GetFeed(feed).Entries;

            foreach (ODataEntry feedEntry in entries)
            {
                this.Materialize(MaterializerEntry.GetEntry(feedEntry), collectionType.ElementType, includeLinks);
            }

            ProjectionPlan continuationPlan = includeLinks ? 
                ODataEntityMaterializer.CreatePlanForDirectMaterialization(property.EntityCollectionItemType) : 
                ODataEntityMaterializer.CreatePlanForShallowMaterialization(property.EntityCollectionItemType);
            
            this.ApplyItemsToCollection(
                entry, 
                property, 
                entries.Select(e => MaterializerEntry.GetEntry(e).ResolvedObject), 
                feed.NextPageLink, 
                continuationPlan,
                false);
        }
 /// <summary>Provides support for Select invocations for projections.</summary>
 /// <param name="materializer">Materializer under which projection is taking place.</param>
 /// <param name="entry">Root entry for paths.</param>
 /// <param name="expectedType">Expected type for <paramref name="entry"/>.</param>
 /// <param name="resultType">Expected result type.</param>
 /// <param name="path">Path to traverse.</param>
 /// <param name="selector">Selector callback.</param>
 /// <returns>An enumerable with the select results.</returns>
 internal static IEnumerable ProjectionSelect(
     object materializer,
     object entry,
     Type expectedType,
     Type resultType,
     object path,
     Func <object, object, Type, object> selector)
 {
     Debug.Assert(typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType()), "typeof(ODataEntityMaterializer).IsAssignableFrom(materializer.GetType())");
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     Debug.Assert(path.GetType() == typeof(ProjectionPath), "path.GetType() == typeof(ProjectionPath)");
     return(ODataEntityMaterializer.ProjectionSelect((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataEntry)entry), expectedType, resultType, (ProjectionPath)path, selector));
 }
        /// <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;
        }
        /// <summary>
        /// Tries to read a feed or entry.
        /// </summary>
        /// <param name="lazy">if set to <c>true</c> [lazy].</param>
        /// <param name="feed">The feed.</param>
        /// <param name="entry">The entry.</param>
        /// <returns>true if a value was read, otherwise false</returns>
        private bool TryReadFeedOrEntry(bool lazy, out ODataFeed feed, out MaterializerEntry entry)
        {
            if (this.TryStartReadFeedOrEntry())
            {
                if (this.reader.State == ODataReaderState.EntryStart)
                {
                    entry = this.ReadEntryCore();
                    feed = null;
                }
                else
                {
                    entry = null;
                    feed = this.ReadFeedCore(lazy);
                }
            }
            else
            {
                feed = null;
                entry = null;
            }

            Debug.Assert(feed == null || entry == null, "feed == null || entry == null");
            return feed != null || entry != null;
        }
Beispiel #35
0
        /// <summary>Materializes the specified <paramref name="entry"/> as dynamic property.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="link">Nested resource link as parsed.</param>
        private void MaterializeDynamicProperty(MaterializerEntry entry, ODataNestedResourceInfo link)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise not resolved/created!");
            Debug.Assert(link != null, "link != null");

            ClientEdmModel model = this.MaterializerContext.Model;

            IDictionary <string, object> containerProperty;

            // Stop if owning type is not an open type
            // Or container property is not found
            // Or key with matching name already exists in the dictionary
            if (!ClientTypeUtil.IsInstanceOfOpenType(entry.ResolvedObject, model) ||
                !ClientTypeUtil.TryGetContainerProperty(entry.ResolvedObject, out containerProperty) ||
                containerProperty.ContainsKey(link.Name))
            {
                return;
            }

            MaterializerNavigationLink linkState = MaterializerNavigationLink.GetLink(link);

            if (linkState == null || (linkState.Entry == null && linkState.Feed == null))
            {
                return;
            }

            // NOTE: ODL (and OData WebApi) support navigational property on complex types
            // That support has not yet been implemented in OData client

            // An entity or entity collection as a dynamic property currently doesn't work as expected
            // due to the absence of a navigational property definition in the metadata
            // to express the relationship between that entity and the parent entity - unless they're the same type!
            // Only materialize a nested resource if its a complex or complex collection

            if (linkState.Feed != null)
            {
                string collectionTypeName     = linkState.Feed.TypeName; // TypeName represents a collection e.g. Collection(NS.Type)
                string collectionItemTypeName = CommonUtil.GetCollectionItemTypeName(collectionTypeName, false);
                // Highly unlikely, but the method return null if the typeName argument does not meet certain expectations
                if (collectionItemTypeName == null)
                {
                    return;
                }

                Type collectionItemType = ResolveClientTypeForDynamicProperty(collectionItemTypeName, entry.ResolvedObject);

                if (collectionItemType != null && ClientTypeUtil.TypeIsComplex(collectionItemType, model))
                {
                    Type  collectionType = typeof(System.Collections.ObjectModel.Collection <>).MakeGenericType(new Type[] { collectionItemType });
                    IList collection     = (IList)Util.ActivatorCreateInstance(collectionType);

                    IEnumerable <ODataResource> feedEntries = MaterializerFeed.GetFeed(linkState.Feed).Entries;
                    foreach (ODataResource feedEntry in feedEntries)
                    {
                        MaterializerEntry linkEntry = MaterializerEntry.GetEntry(feedEntry);
                        this.Materialize(linkEntry, collectionItemType, false /*includeLinks*/);
                        collection.Add(linkEntry.ResolvedObject);
                    }
                    containerProperty.Add(link.Name, collection);
                }
            }
            else
            {
                MaterializerEntry linkEntry = linkState.Entry;
                Type itemType = ResolveClientTypeForDynamicProperty(linkEntry.Entry.TypeName, entry.ResolvedObject);

                if (itemType != null && ClientTypeUtil.TypeIsComplex(itemType, model))
                {
                    this.Materialize(linkEntry, itemType, false /*includeLinks*/);
                    containerProperty.Add(link.Name, linkEntry.ResolvedObject);
                }
            }
        }
        /// <summary>
        /// Tries to resolve the specified entry as an entry that has already been created in this materialization session or is already in the context.
        /// </summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="expectedEntryType">Expected type of the entry.</param>
        /// <returns>True if the entry was resolved, otherwise False.</returns>
        internal bool TryResolveAsExistingEntry(MaterializerEntry entry, Type expectedEntryType)
        {
            if (entry.Entry.IsTransient)
            {
                return false;
            }

            // Resolution is based on the entry Id, so if we can't use that property, we don't even need to try to resolve it.
            if (entry.IsAtomOrTracking)
            {
                // The resolver methods below will both need access to Id, so first ensure it's not null
                if (entry.Id == null)
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MissingIdElement);
                }

                return this.TryResolveAsCreated(entry) || this.TryResolveFromContext(entry, expectedEntryType);
            }

            return false;
        }
        /// <summary>Attempts to resolve an entry from those tracked in the log.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="existingEntry">
        /// After invocation, an existing entry with the same identity as 
        /// <paramref name="entry"/>; possibly null.
        /// </param>
        /// <returns>true if an existing entry was found; false otherwise.</returns>
        internal bool TryResolve(MaterializerEntry entry, out MaterializerEntry existingEntry)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.Id != null, "entry.Id != null");
            Debug.Assert(entry.IsAtomOrTracking, "Should not be trying to resolve the entry if entry.IsAtomOrTracking is false.");

            ODataEntry existingODataEntry;

            if (this.identityStack.TryGetValue(entry.Id, out existingODataEntry))
            {
                existingEntry = MaterializerEntry.GetEntry(existingODataEntry);
                return true;
            }

            if (this.appendOnlyEntries.TryGetValue(entry.Id, out existingODataEntry))
            {
                // The AppendOnly entries are valid only as long as they were not modified
                // between calls to .MoveNext().
                EntityStates state;
                this.entityTracker.TryGetEntity(entry.Id, out state);
                if (state == EntityStates.Unchanged)
                {
                    existingEntry = MaterializerEntry.GetEntry(existingODataEntry);
                    return true;
                }
                else
                {
                    this.appendOnlyEntries.Remove(entry.Id);
                }
            }

            existingEntry = default(MaterializerEntry);
            return false;
        }
 /// <summary>"Resolved" the entity in the <paramref name="entry"/> by instantiating it.</summary>
 /// <param name="entry">Entry to resolve.</param>
 /// <param name="type">Type to create.</param>
 /// <remarks>
 /// After invocation, entry.ResolvedObject is exactly of type <paramref name="type"/>.
 /// </remarks>
 internal void ResolveByCreatingWithType(MaterializerEntry entry, Type type)
 {
     // TODO: CreateNewInstance needs to do all of these operations otherwise an inadvertent call to CreateNewInstance
     // will create a new entity instance that is not tracked in the context or materialization log. Will need to change this
     // prior to shipping if public
     Debug.Assert(
          entry.ResolvedObject == null,
          "entry.ResolvedObject == null -- otherwise we're about to overwrite - should never be called");
     ClientEdmModel edmModel = this.MaterializerContext.Model;
     entry.ActualType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(type));
     entry.ResolvedObject = this.CreateNewInstance(entry.ActualType.EdmTypeReference, type);
     entry.CreatedByMaterializer = true;
     entry.ShouldUpdateFromPayload = true;
     entry.EntityHasBeenResolved = true;
     this.EntityTrackingAdapter.MaterializationLog.CreatedInstance(entry);
 }
        /// <summary>
        /// Invoke this method to notify the log that the
        /// target instance of a "directed" update was found.
        /// </summary>
        /// <param name="entry">Entry found.</param>
        /// <remarks>
        /// The target instance is typically the object that we
        /// expect will get refreshed by the response from a POST
        /// method.
        /// 
        /// For example if a create a Customer and POST it to
        /// a service, the response of the POST will return the
        /// re-serialized instance, with (important!) server generated
        /// values and URIs.
        /// </remarks>
        internal void FoundTargetInstance(MaterializerEntry entry)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise this is not a target");
            
            if (IsEntity(entry))
            {
                Debug.Assert(entry.IsAtomOrTracking, "entry.IsAtomOrTracking == true, otherwise we should not be tracking this entry with the context.");

                this.entityTracker.AttachIdentity(entry.EntityDescriptor, this.mergeOption);
                this.identityStack.Add(entry.Id, entry.Entry);
                this.insertRefreshObject = entry.ResolvedObject;
            }
        }
        /// <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);
                    }
                }
            }
        }
Beispiel #41
0
        /// <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, "ODataResource 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>Materializes the specified <paramref name="entry"/>.</summary>
        /// <param name="entry">Entry with object to materialize.</param>
        /// <param name="expectedEntryType">Expected type for the entry.</param>
        /// <param name="includeLinks">Whether links that are expanded should be materialized.</param>
        /// <remarks>This is a payload-driven materialization process.</remarks>
        internal void Materialize(MaterializerEntry entry, Type expectedEntryType, bool includeLinks)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(
                entry.ResolvedObject == null || entry.ResolvedObject == this.EntityTrackingAdapter.TargetInstance,
                "entry.ResolvedObject == null || entry.ResolvedObject == this.targetInstance -- otherwise getting called twice");
            Debug.Assert(expectedEntryType != null, "expectedType != null");

            // ResolvedObject will already be assigned when we have a TargetInstance, for example.
            if (!this.EntityTrackingAdapter.TryResolveExistingEntity(entry, expectedEntryType))
            {
                // If the type is a derived one this call will resolve to derived type, cannot put code in  ResolveByCreatingWithType as this is used by projection and in this case
                // the type cannot be changed or updated
                ClientTypeAnnotation actualType = this.MaterializerContext.ResolveTypeForMaterialization(expectedEntryType, entry.Entry.TypeName);

                this.ResolveByCreatingWithType(entry, actualType.ElementType);
            }

            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise ResolveOrCreateInstnace didn't do its job");

            this.MaterializeResolvedEntry(entry, includeLinks);
        }
        /// <summary>Tries to resolve the object from those created in this materialization session.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveAsCreated(MaterializerEntry entry)
        {
            Debug.Assert(entry.IsAtomOrTracking, "Should not be trying to resolve the entry from the current materialization session if entry.IsAtomOrTracking is false.");

            MaterializerEntry existingEntry;
            if (!this.MaterializationLog.TryResolve(entry, out existingEntry))
            {
                return false;
            }

            Debug.Assert(
                existingEntry.ResolvedObject != null,
                "existingEntry.ResolvedObject != null -- how did it get there otherwise?");
            entry.ActualType = existingEntry.ActualType;
            entry.ResolvedObject = existingEntry.ResolvedObject;
            entry.CreatedByMaterializer = existingEntry.CreatedByMaterializer;
            entry.ShouldUpdateFromPayload = existingEntry.ShouldUpdateFromPayload;
            entry.EntityHasBeenResolved = true;
            return true;
        }
 /// <summary>
 /// Returns true the specified entry represents an entity.
 /// </summary>
 /// <param name="entry">The materializer entry</param>
 /// <returns>True if the entry represents an entity.</returns>
 private static bool IsEntity(MaterializerEntry entry)
 {
     Debug.Assert(entry.ActualType != null, "Entry with no type added to log");
     return entry.ActualType.IsEntityType;
 }
        /// <summary>Tries to resolve the object as one from the context (only if tracking is enabled).</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="expectedEntryType">Expected entry type for the specified <paramref name="entry"/>.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveFromContext(MaterializerEntry entry, Type expectedEntryType)
        {
            Debug.Assert(entry.IsAtomOrTracking, "Should not be trying to resolve the entry from the context if entry.IsAtomOrTracking is false.");

            // We should either create a new instance or grab one from the context.
            if (this.MergeOption != MergeOption.NoTracking)
            {
                EntityStates state;
                entry.ResolvedObject = this.EntityTracker.TryGetEntity(entry.Id, out state);
                if (entry.ResolvedObject != null)
                {
                    if (!expectedEntryType.IsInstanceOfType(entry.ResolvedObject))
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_Current(expectedEntryType, entry.ResolvedObject.GetType()));
                    }

                    ClientEdmModel edmModel = this.Model;
                    entry.ActualType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(entry.ResolvedObject.GetType()));
                    entry.EntityHasBeenResolved = true;

                    // Note that deleted items will have their properties overwritten even
                    // if PreserveChanges is used as a merge option.
                    entry.ShouldUpdateFromPayload =
                        this.MergeOption == MergeOption.OverwriteChanges ||
                       (this.MergeOption == MergeOption.PreserveChanges && state == EntityStates.Unchanged) ||
                       (this.MergeOption == MergeOption.PreserveChanges && state == EntityStates.Deleted);
                    
                    this.MaterializationLog.FoundExistingInstance(entry);

                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Invoke this method to notify the log that a link was set on
        /// a property.
        /// </summary>
        /// <param name="source">Entry for source object.</param>
        /// <param name="propertyName">Name of property set.</param>
        /// <param name="target">Target object.</param>
        internal void SetLink(MaterializerEntry source, string propertyName, object target)
        {
            Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty");
            Debug.Assert(propertyName != null, "propertyName != null");

            if (!this.Tracking)
            {
                return;
            }

            if (IsEntity(source) && IsEntity(target, this.clientEdmModel))
            {
                Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers");
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Modified);
                this.links.Add(item);
            }
        }
        /// <summary>Tries to resolve the object as the target one in a POST refresh.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <returns>true if the entity was resolved; false otherwise.</returns>
        private bool TryResolveAsTarget(MaterializerEntry entry)
        {
            if (entry.ResolvedObject == null)
            {
                return false;
            }

            // The only case when the entity hasn't been resolved but
            // it has already been set is when the target instance
            // was set directly to refresh a POST.
            Debug.Assert(
                entry.ResolvedObject == this.TargetInstance,
                "entry.ResolvedObject == this.TargetInstance -- otherwise there we ResolveOrCreateInstance more than once on the same entry");
            Debug.Assert(
                this.MergeOption == MergeOption.OverwriteChanges || this.MergeOption == MergeOption.PreserveChanges,
                "MergeOption.OverwriteChanges and MergeOption.PreserveChanges are the only expected values during SaveChanges");
            ClientEdmModel edmModel = this.Model;
            entry.ActualType = edmModel.GetClientTypeAnnotation(edmModel.GetOrCreateEdmType(entry.ResolvedObject.GetType()));
            this.MaterializationLog.FoundTargetInstance(entry);
            entry.ShouldUpdateFromPayload = this.MergeOption != MergeOption.PreserveChanges;
            entry.EntityHasBeenResolved = true;
            return true;
        }
        /// <summary>
        /// Invoke this method to notify the log that a new instance
        /// was created.
        /// </summary>
        /// <param name="entry">Entry for the created instance.</param>
        internal void CreatedInstance(MaterializerEntry entry)
        {
            Debug.Assert(entry.Entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise, what did we create?");
            Debug.Assert(entry.CreatedByMaterializer, "entry.CreatedByMaterializer -- otherwise we shouldn't be calling this");

            if (IsEntity(entry) && entry.IsAtomOrTracking && !entry.Entry.IsTransient)
            {
                this.identityStack.Add(entry.Id, entry.Entry);
                if (this.mergeOption == MergeOption.AppendOnly)
                {
                    this.appendOnlyEntries.Add(entry.Id, entry.Entry);
                }
            }
        }
 /// <summary>Provides support for getting payload entries during projections.</summary>
 /// <param name="entry">Entry to get sub-entry from.</param>
 /// <param name="name">Name of sub-entry.</param>
 /// <returns>The sub-entry (never null).</returns>
 internal static object ProjectionGetEntry(object entry, string name)
 {
     Debug.Assert(entry.GetType() == typeof(ODataEntry), "entry.GetType() == typeof(ODataEntry)");
     return(ODataEntityMaterializer.ProjectionGetEntry(MaterializerEntry.GetEntry((ODataEntry)entry), name));
 }
        /// <summary>
        /// Materializes the link properties if any with the url in the response payload
        /// </summary>
        /// <param name="actualType">Actual client type that is getting materialized.</param>
        /// <param name="entry">MaterializerEntry instance containing all the links that came in the response.</param>
        private static void ApplyLinkProperties(
            ClientTypeAnnotation actualType,
            MaterializerEntry entry)
        {
            Debug.Assert(actualType != null, "actualType != null");
            Debug.Assert(entry.Entry != null, "entry != null");

            if (entry.ShouldUpdateFromPayload)
            {
                foreach (var linkProperty in actualType.Properties().Where(p => p.PropertyType == typeof(DataServiceStreamLink)))
                {
                    string propertyName = linkProperty.PropertyName;
                    StreamDescriptor streamDescriptor;
                    if (entry.EntityDescriptor.TryGetNamedStreamInfo(propertyName, out streamDescriptor))
                    {
                        // At this time we have materialized the stream link onto the stream descriptor object
                        // we'll always make sure the stream link is the same instance on the entity and the descriptor
                        linkProperty.SetValue(entry.ResolvedObject, streamDescriptor.StreamLink, propertyName, true /*allowAdd*/);
                    }
                }
            }
        }
 /// <summary>
 /// Tries to read an entry.
 /// </summary>
 /// <param name="entry">The entry.</param>
 /// <returns>true if a value was read, otherwise false</returns>
 private bool TryReadEntry(out MaterializerEntry entry)
 {
     if (this.TryStartReadFeedOrEntry())
     {
         this.ExpectState(ODataReaderState.EntryStart);
         entry = this.ReadEntryCore();
         return true;
     }
     else
     {
         entry = default(MaterializerEntry);
         return false;
     }
 }
Beispiel #52
0
        /// <summary>
        /// Creates the materializer entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="format">The format the entry was read in.</param>
        /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param>
        /// <param name="model">The client model.</param>
        /// <returns>A new materializer entry.</returns>
        public static MaterializerEntry CreateEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model)
        {
            Debug.Assert(entry.GetAnnotation<MaterializerEntry>() == null, "MaterializerEntry has already been created.");

            MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model);
            entry.SetAnnotation<MaterializerEntry>(materializerEntry);

            return materializerEntry;
        }