#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); }
/// <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); }
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; }
/// <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); } } }