/// <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(ODataResource), "entry.GetType() == typeof(ODataResource)");
     return(ODataEntityMaterializer.ProjectionInitializeEntity((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedType, resultType, properties, propertyValues));
 }
 /// <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(ODataResource), "entry.GetType() == typeof(ODataResource)");
     return(ODataEntityMaterializer.ShallowMaterializePlan((ODataEntityMaterializer)materializer, MaterializerEntry.GetEntry((ODataResource)entry), expectedEntryType));
 }
        /// <summary>Applies all accumulated changes to the associated data context.</summary>
        /// <remarks>The log should be cleared after this method successfully executed.</remarks>
        internal void ApplyToContext()
        {
            if (!this.Tracking)
            {
                return;
            }

            foreach (KeyValuePair <Uri, ODataResource> entity in this.identityStack)
            {
                // Try to attach the entity descriptor got from materializer, if one already exists, get the existing reference instead.
                MaterializerEntry entry = MaterializerEntry.GetEntry(entity.Value);

                bool mergeEntityDescriptorInfo = entry.CreatedByMaterializer ||
                                                 entry.ResolvedObject == this.insertRefreshObject ||
                                                 entry.ShouldUpdateFromPayload;

                // Whenever we merge the data, only at those times will be merge the links also
                EntityDescriptor descriptor = this.entityTracker.InternalAttachEntityDescriptor(entry.EntityDescriptor, false /*failIfDuplicated*/);
                AtomMaterializerLog.MergeEntityDescriptorInfo(descriptor, entry.EntityDescriptor, mergeEntityDescriptorInfo, this.mergeOption);

                if (mergeEntityDescriptorInfo)
                {
                    // In AtomMaterializer.TryResolveFromContext, we set AtomEntry.ShouldUpdateFromPayload to true
                    // when even MergeOption is PreserveChanges and entityState is Deleted. But in that case, we cannot
                    // set the entity state to Unchanged, hence need to workaround that one scenario
                    if (this.mergeOption != MergeOption.PreserveChanges || descriptor.State != EntityStates.Deleted)
                    {
                        // we should always reset descriptor's state to Unchanged (old v1 behavior)
                        descriptor.State = EntityStates.Unchanged;
                        descriptor.PropertiesToSerialize.Clear();
                    }
                }
            }

            foreach (LinkDescriptor link in this.links)
            {
                if (EntityStates.Added == link.State)
                {
                    // Added implies collection
                    this.entityTracker.AttachLink(link.Source, link.SourceProperty, link.Target, this.mergeOption);
                }
                else if (EntityStates.Modified == link.State)
                {
                    // Modified implies reference
                    object target = link.Target;
                    if (MergeOption.PreserveChanges == this.mergeOption)
                    {
                        // GetLinks looks up the existing link using just the SourceProperty, the declaring server type name is not significant here.
                        LinkDescriptor end = this.entityTracker.GetLinks(link.Source, link.SourceProperty).SingleOrDefault();
                        if (end != null && end.Target == null)
                        {
                            // leave the SetLink(link.Source, link.SourceProperty, null)
                            continue;
                        }

                        if ((target != null) && (this.entityTracker.GetEntityDescriptor(target).State == EntityStates.Deleted) ||
                            (EntityStates.Deleted == this.entityTracker.GetEntityDescriptor(link.Source).State))
                        {
                            target = null;
                        }
                    }

                    this.entityTracker.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption);
                }
                else
                {
                    // detach link
                    Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                    this.entityTracker.DetachExistingLink(link, false);
                }
            }
        }