#pragma warning restore 649
#endif

        #endregion Private fields.

        internal MaterializeAtom(DataServiceContext context, XmlReader reader, QueryComponents queryComponents, ProjectionPlan plan, MergeOption mergeOption)
        {
            Debug.Assert(queryComponents != null, "queryComponents != null");

            this.context                 = context;
            this.elementType             = queryComponents.LastSegmentType;
            this.MergeOptionValue        = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties;
            this.reader               = (reader == null) ? null : new System.Data.Services.Client.Xml.XmlAtomErrorReader(reader);
            this.countValue           = CountStateInitial;
            this.expectingSingleValue = ClientConvert.IsKnownNullableType(elementType);

            Debug.Assert(reader != null, "Materializer reader is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?");

            reader.Settings.NameTable.Add(context.DataNamespace);

            string typeScheme = this.context.TypeScheme.OriginalString;

            this.parser = new AtomParser(this.reader, AtomParser.XElementBuilderCallback, typeScheme, context.DataNamespace);
            AtomMaterializerLog log = new AtomMaterializerLog(this.context, mergeOption);
            Type implementationType;
            Type materializerType = GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);

            this.materializer = new AtomMaterializer(parser, context, materializerType, this.ignoreMissingProperties, mergeOption, log, this.MaterializedObjectCallback, queryComponents, plan);
        }
Beispiel #2
0
        /// <summary>response materialization has an identity to attach to the inserted object</summary>
        /// <param name="entityDescriptorFromMaterializer">entity descriptor containing all the information about the entity from the response.</param>
        /// <param name="metadataMergeOption">mergeOption based on which EntityDescriptor will be merged.</param>
        internal override void AttachIdentity(EntityDescriptor entityDescriptorFromMaterializer, MergeOption metadataMergeOption)
        {   // insert->unchanged
            Debug.Assert(entityDescriptorFromMaterializer != null, "entityDescriptorFromMaterializer != null");

            this.EnsureIdentityToResource();

            // resource.State == EntityState.Added or Unchanged for second pass of media link
            EntityDescriptor trackedEntityDescriptor = this.entityDescriptors[entityDescriptorFromMaterializer.Entity];

            // make sure we got the right one - server could override identity and we may be tracking another one already.
            this.ValidateDuplicateIdentity(entityDescriptorFromMaterializer.Identity, trackedEntityDescriptor);

            this.DetachResourceIdentity(trackedEntityDescriptor);

            // While processing the response, we need to find out if the given resource was inserted deep
            // If it was, then we need to change the link state from added to unchanged
            if (trackedEntityDescriptor.IsDeepInsert)
            {
                LinkDescriptor end = this.bindings[trackedEntityDescriptor.GetRelatedEnd()];
                end.State = EntityStates.Unchanged;
            }

            trackedEntityDescriptor.Identity = entityDescriptorFromMaterializer.Identity; // always attach the identity
            AtomMaterializerLog.MergeEntityDescriptorInfo(trackedEntityDescriptor, entityDescriptorFromMaterializer, true /*mergeInfo*/, metadataMergeOption);
            trackedEntityDescriptor.State = EntityStates.Unchanged;

            // scenario: sucessfully (1) delete an existing entity and (2) add a new entity where the new entity has the same identity as deleted entity
            // where the SaveChanges pass1 will now associate existing identity with new entity
            // but pass2 for the deleted entity will not blindly remove the identity that is now associated with the new identity
            this.identityToDescriptor[entityDescriptorFromMaterializer.Identity] = trackedEntityDescriptor;
        }
#pragma warning restore 649
#endif

        #endregion Private fields.

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="context">originating context</param>
        /// <param name="reader">reader</param>
        /// <param name="queryComponents">Query components (projection, expected type)</param>
        /// <param name="plan">Projection plan (if compiled in an earlier query).</param>
        /// <param name="mergeOption">merge option to use for this materialization pass</param>
        internal MaterializeAtom(DataServiceContext context, XmlReader reader, QueryComponents queryComponents, ProjectionPlan plan, MergeOption mergeOption)
        {
            Debug.Assert(queryComponents != null, "queryComponents != null");

            this.context                 = context;
            this.elementType             = queryComponents.LastSegmentType;
            this.MergeOptionValue        = mergeOption;
            this.ignoreMissingProperties = context.IgnoreMissingProperties;
            this.reader               = (reader == null) ? null : new System.Data.Services.Client.Xml.XmlAtomErrorReader(reader);
            this.countValue           = CountStateInitial;
            this.expectingSingleValue = ClientConvert.IsKnownNullableType(elementType);

            Debug.Assert(reader != null, "Materializer reader is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?");

            // NOTE: dataNamespace is used for reference equality, and while it looks like
            // a variable, it appears that it will only get set to XmlConstants.DataWebNamespace
            // at runtime. Therefore we remove string dataNamespace as a field here.
            // this.dataNamespace = reader != null ? reader.Settings.NameTable.Add(context.DataNamespace) : null;
            reader.Settings.NameTable.Add(context.DataNamespace);

            string typeScheme = this.context.TypeScheme.OriginalString;

            this.parser = new AtomParser(this.reader, AtomParser.XElementBuilderCallback, typeScheme, context.DataNamespace);
            AtomMaterializerLog log = new AtomMaterializerLog(this.context, mergeOption);
            Type implementationType;
            Type materializerType = GetTypeForMaterializer(this.expectingSingleValue, this.elementType, out implementationType);

            this.materializer = new AtomMaterializer(parser, context, materializerType, this.ignoreMissingProperties, mergeOption, log, this.MaterializedObjectCallback, queryComponents, plan);
        }
Beispiel #4
0
        internal void AttachIdentity(EntityDescriptor entityDescriptorFromMaterializer, MergeOption metadataMergeOption)
        {
            this.EnsureIdentityToResource();
            EntityDescriptor descriptor = this.entityDescriptors[entityDescriptorFromMaterializer.Entity];

            this.ValidateDuplicateIdentity(entityDescriptorFromMaterializer.Identity, descriptor);
            this.DetachResourceIdentity(descriptor);
            if (descriptor.IsDeepInsert)
            {
                LinkDescriptor descriptor2 = this.bindings[descriptor.GetRelatedEnd(this.maxProtocolVersion)];
                descriptor2.State = EntityStates.Unchanged;
            }
            descriptor.Identity = entityDescriptorFromMaterializer.Identity;
            AtomMaterializerLog.MergeEntityDescriptorInfo(descriptor, entityDescriptorFromMaterializer, true, metadataMergeOption);
            descriptor.State = EntityStates.Unchanged;
            this.identityToDescriptor[entityDescriptorFromMaterializer.Identity] = descriptor;
        }
Beispiel #5
0
        /// <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 <String, ODataEntry> 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 behaviour)
                        descriptor.State = EntityStates.Unchanged;
                    }
                }
            }

            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 (null != end && null == end.Target)
                        {
                            // leave the SetLink(link.Source, link.SourceProperty, null)
                            continue;
                        }

                        if ((null != target) && (EntityStates.Deleted == this.entityTracker.GetEntityDescriptor(target).State) ||
                            (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);
                }
            }
        }