/// <summary>use location from header to generate initial edit and identity</summary> /// <param name="entity">entity in added state</param> /// <param name="identity">identity as specified in the response header - location header or dataserviceid header.</param> /// <param name="editLink">editlink as specified in the response header - location header.</param> internal void AttachLocation(object entity, string identity, Uri editLink) { Debug.Assert(null != entity, "null != entity"); Debug.Assert(!String.IsNullOrEmpty(identity), "!String.IsNullOrEmpty(identity)"); Debug.Assert(editLink != null, "editLink != null"); this.EnsureIdentityToResource(); // resource.State == EntityState.Added or Unchanged for second pass of media link EntityDescriptor resource = this.entityDescriptors[entity]; // make sure we got the right one - server could override identity and we may be tracking another one already. this.ValidateDuplicateIdentity(identity, resource); this.DetachResourceIdentity(resource); // 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 (resource.IsDeepInsert) { LinkDescriptor end = this.bindings[resource.GetRelatedEnd()]; end.State = EntityStates.Unchanged; } resource.Identity = identity; // always attach the identity resource.EditLink = editLink; // scenario: sucessfully batch (1) add a new entity and (2) delete an existing 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[identity] = resource; }
/// <summary> /// Writes an entity reference link. /// </summary> /// <param name="binding">The link descriptor.</param> /// <param name="requestMessage">The request message used for writing the payload.</param> internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage) #endif { using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/)) { EntityDescriptor targetResource = this.requestInfo.EntityTracker.GetEntityDescriptor(binding.Target); Uri targetResourceEditLink; if (null != targetResource.GetLatestIdentity()) { // When we write the uri in the payload, we need to make sure that we write the edit // link in the payload, since the request uri is the edit link of the parent entity. // Think of a read/write service - since the uri is the target link to the parent entity // its better that we write the edit link of the child entity in the payload. targetResourceEditLink = targetResource.GetResourceUri(this.requestInfo.BaseUriResolver, false /*queryLink*/); } else { #if DEBUG Debug.Assert(isBatch, "we should be cross-referencing entities only in batch scenarios"); #endif targetResourceEditLink = UriUtil.CreateUri("$" + targetResource.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative); } ODataEntityReferenceLink referenceLink = new ODataEntityReferenceLink(); referenceLink.Url = targetResourceEditLink; messageWriter.WriteEntityReferenceLink(referenceLink); } }
/// <summary>Detach existing link</summary> /// <param name="existingLink">link to detach</param> /// <param name="targetDelete">true if target is being deleted, false otherwise</param> internal override void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete) { // The target can be null in which case we don't need this check if (existingLink.Target != null) { // Identify the target resource for the link EntityDescriptor targetResource = this.GetEntityDescriptor(existingLink.Target); // Check if there is a dependency relationship b/w the source and target objects i.e. target can not exist without source link // Deep insert requires this check to be made but skip the check if the target object is being deleted if (targetResource.IsDeepInsert && !targetDelete) { EntityDescriptor parentOfTarget = targetResource.ParentForInsert; if (Object.ReferenceEquals(targetResource.ParentEntity, existingLink.Source) && (parentOfTarget.State != EntityStates.Deleted || parentOfTarget.State != EntityStates.Detached)) { throw new InvalidOperationException(Strings.Context_ChildResourceExists); } } } if (this.TryRemoveLinkDescriptor(existingLink)) { // this link may have been previously detached by a detaching entity existingLink.State = EntityStates.Detached; } }
/// <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; }
internal bool IsRelatedEntity(LinkDescriptor related) { if (this.entity != related.Source) { return(this.entity == related.Target); } return(true); }
internal void AddedLink(MaterializerEntry source, string propertyName, object target) { if (this.Tracking && (ShouldTrackWithContext(source) && ShouldTrackWithContext(target, this.responseInfo.MaxProtocolVersion))) { LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); this.links.Add(item); } }
private static void HandleResponsePost(LinkDescriptor linkDescriptor) { if ((EntityStates.Added != linkDescriptor.State) && ((EntityStates.Modified != linkDescriptor.State) || (linkDescriptor.Target == null))) { System.Data.Services.Client.Error.ThrowBatchUnexpectedContent(InternalError.LinkNotAddedState); } linkDescriptor.State = EntityStates.Unchanged; }
internal void ApplyToContext() { if (this.Tracking) { foreach (KeyValuePair <string, ODataEntry> pair in this.identityStack) { MaterializerEntry entry = MaterializerEntry.GetEntry(pair.Value); bool mergeInfo = (entry.CreatedByMaterializer || (entry.ResolvedObject == this.insertRefreshObject)) || entry.ShouldUpdateFromPayload; EntityDescriptor trackedEntityDescriptor = this.responseInfo.EntityTracker.InternalAttachEntityDescriptor(entry.EntityDescriptor, false); MergeEntityDescriptorInfo(trackedEntityDescriptor, entry.EntityDescriptor, mergeInfo, this.mergeOption); if (mergeInfo && ((this.responseInfo.MergeOption != MergeOption.PreserveChanges) || (trackedEntityDescriptor.State != EntityStates.Deleted))) { trackedEntityDescriptor.State = EntityStates.Unchanged; } } foreach (LinkDescriptor descriptor2 in this.links) { if (EntityStates.Added == descriptor2.State) { if ((EntityStates.Deleted == this.responseInfo.EntityTracker.GetEntityDescriptor(descriptor2.Target).State) || (EntityStates.Deleted == this.responseInfo.EntityTracker.GetEntityDescriptor(descriptor2.Source).State)) { this.responseInfo.EntityTracker.DetachExistingLink(descriptor2, false); } else { this.responseInfo.EntityTracker.AttachLink(descriptor2.Source, descriptor2.SourceProperty, descriptor2.Target, this.mergeOption); } } else { if (EntityStates.Modified == descriptor2.State) { object target = descriptor2.Target; if (MergeOption.PreserveChanges == this.mergeOption) { LinkDescriptor descriptor3 = this.responseInfo.EntityTracker.GetLinks(descriptor2.Source, descriptor2.SourceProperty).SingleOrDefault <LinkDescriptor>(); if ((descriptor3 != null) && (descriptor3.Target == null)) { goto Label_0233; } if (((target != null) && (EntityStates.Deleted == this.responseInfo.EntityTracker.GetEntityDescriptor(target).State)) || (EntityStates.Deleted == this.responseInfo.EntityTracker.GetEntityDescriptor(descriptor2.Source).State)) { target = null; } } this.responseInfo.EntityTracker.AttachLink(descriptor2.Source, descriptor2.SourceProperty, target, this.mergeOption); } else { this.responseInfo.EntityTracker.DetachExistingLink(descriptor2, false); } Label_0233 :; } } } }
protected Uri CreateRequestRelativeUri(LinkDescriptor binding) { if (binding.IsSourcePropertyCollection && (EntityStates.Added != binding.State)) { EntityDescriptor entityDescriptor = this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Target); Uri uri = DataServiceContext.GenerateEditLinkRelativeUri(binding.SourceProperty, entityDescriptor.Entity, this.RequestInfo.MaxProtocolVersion); return(Util.CreateUri("$links" + '/' + CommonUtil.UriToString(uri), UriKind.Relative)); } return(Util.CreateUri("$links" + '/' + binding.SourceProperty, UriKind.Relative)); }
internal void AddLink(LinkDescriptor linkDescriptor) { try { this.bindings.Add(linkDescriptor, linkDescriptor); } catch (ArgumentException) { throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_RelationAlreadyContained); } }
/// <summary> /// Invoke this method to notify the log that a link was removed /// from a collection. /// </summary> /// <param name="source"> /// Instance with the collection from which <paramref name="target"/> /// was removed. /// </param> /// <param name="propertyName">Property name for collection.</param> /// <param name="target">Object which was removed.</param> internal void RemovedLink(MaterializerEntry source, string propertyName, object target) { Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty"); Debug.Assert(propertyName != null, "propertyName != null"); 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.Detached); this.links.Add(item); } }
/// <summary> /// Invoke this method to notify the log that a link was removed /// from a collection. /// </summary> /// <param name="source"> /// Instance with the collection from which <paramref name="target"/> /// was removed. /// </param> /// <param name="propertyName">Property name for collection.</param> /// <param name="target">Object which was removed.</param> internal void RemovedLink(AtomEntry source, string propertyName, object target) { Debug.Assert(source != null, "source != null"); Debug.Assert(propertyName != null, "propertyName != null"); if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) { 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.Detached); this.links.Add(item); } }
internal static LinkDescriptor RestoreState( LinkDescriptorState state, Dictionary <Guid, EntityDescriptor> idToEntityDescriptor) { var linkDescriptor = new LinkDescriptor( idToEntityDescriptor[state.SourceDescriptorId].Entity, state.SourceProperty, idToEntityDescriptor[state.TargetDescriptorId].Entity); linkDescriptor.RestoreState((DescriptorState)state); return(linkDescriptor); }
/// <summary> /// Add the given link to the link descriptor collection /// </summary> /// <param name="linkDescriptor">link descriptor to add</param> /// <exception cref="InvalidOperationException">throws argument exception if the link already exists</exception> internal void AddLink(LinkDescriptor linkDescriptor) { Debug.Assert(linkDescriptor != null, "linkDescriptor != null"); try { this.EnsureLinkBindings(); this.bindings.Add(linkDescriptor, linkDescriptor); } catch (ArgumentException) { throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained); } }
internal LinkDescriptor DetachReferenceLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor existingLink = this.GetLinks(source, sourceProperty).FirstOrDefault <LinkDescriptor>(); if (existingLink != null) { if (((target == existingLink.Target) || (linkMerge == MergeOption.AppendOnly)) || ((MergeOption.PreserveChanges == linkMerge) && (EntityStates.Modified == existingLink.State))) { return(existingLink); } this.DetachExistingLink(existingLink, false); } return(null); }
/// <summary> /// attach the link with the given source, sourceProperty and target. /// </summary> /// <param name="source">source entity of the link.</param> /// <param name="sourceProperty">name of the property on the source entity.</param> /// <param name="target">target entity of the link.</param> /// <param name="linkMerge">merge option to be used to merge the link if there is an existing link.</param> internal override void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target, this.model); LinkDescriptor existing = this.TryGetLinkDescriptor(source, sourceProperty, target); if (existing != null) { switch (linkMerge) { case MergeOption.AppendOnly: break; case MergeOption.OverwriteChanges: relation = existing; break; case MergeOption.PreserveChanges: if ((EntityStates.Added == existing.State) || (EntityStates.Unchanged == existing.State) || (EntityStates.Modified == existing.State && null != existing.Target)) { relation = existing; } break; case MergeOption.NoTracking: // public API point should throw if link exists throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained); } } else { if (this.model.GetClientTypeAnnotation(this.model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection || (null == (existing = this.DetachReferenceLink(source, sourceProperty, target, linkMerge)))) { this.AddLink(relation); this.IncrementChange(relation); } else if (!((MergeOption.AppendOnly == linkMerge) || (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State))) { // AppendOnly doesn't change state or target // OverWriteChanges changes target and state // PreserveChanges changes target if unchanged, leaves modified target and state alone relation = existing; } } relation.State = EntityStates.Unchanged; }
protected Uri CreateRequestUri(EntityDescriptor sourceResource, LinkDescriptor binding) { Uri uri; LinkInfo linkInfo = null; if (sourceResource.TryGetLinkInfo(binding.SourceProperty, out linkInfo) && ((uri = linkInfo.AssociationLink) != null)) { if (binding.IsSourcePropertyCollection && (EntityStates.Deleted == binding.State)) { EntityDescriptor entityDescriptor = this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Target); uri = DataServiceContext.AppendKeysToUri(uri.AbsoluteUri, entityDescriptor.Entity, UriKind.Absolute, this.RequestInfo.MaxProtocolVersion); } return(uri); } return(Util.CreateUri(sourceResource.GetResourceUri(this.RequestInfo.BaseUriResolver, false), this.CreateRequestRelativeUri(binding))); }
protected static string GetLinkHttpMethod(LinkDescriptor link) { if (!link.IsSourcePropertyCollection) { if (link.Target == null) { return("DELETE"); } return("PUT"); } if (EntityStates.Deleted == link.State) { return("DELETE"); } return("POST"); }
internal void AttachLocation(object entity, string identity, Uri editLink) { this.EnsureIdentityToResource(); EntityDescriptor descriptor = this.entityDescriptors[entity]; this.ValidateDuplicateIdentity(identity, descriptor); this.DetachResourceIdentity(descriptor); if (descriptor.IsDeepInsert) { LinkDescriptor descriptor2 = this.bindings[descriptor.GetRelatedEnd(this.maxProtocolVersion)]; descriptor2.State = EntityStates.Unchanged; } descriptor.Identity = identity; descriptor.EditLink = editLink; this.identityToDescriptor[identity] = descriptor; }
/// <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(AtomEntry source, string propertyName, object target) { Debug.Assert(source != null, "source != null"); Debug.Assert(propertyName != null, "propertyName != null"); if (!this.Tracking) { return; } if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) { LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); this.links.Add(item); } }
/// <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); } }
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; }
internal void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete) { if (existingLink.Target != null) { EntityDescriptor entityDescriptor = this.GetEntityDescriptor(existingLink.Target); if (entityDescriptor.IsDeepInsert && !targetDelete) { EntityDescriptor parentForInsert = entityDescriptor.ParentForInsert; if (object.ReferenceEquals(entityDescriptor.ParentEntity, existingLink.Source) && ((parentForInsert.State != EntityStates.Deleted) || (parentForInsert.State != EntityStates.Detached))) { throw new InvalidOperationException(System.Data.Services.Client.Strings.Context_ChildResourceExists); } } } if (this.TryRemoveLinkDescriptor(existingLink)) { existingLink.State = EntityStates.Detached; } }
protected ODataRequestMessageWrapper CreateRequest(LinkDescriptor binding) { if (binding.ContentGeneratedForSave) { return(null); } EntityDescriptor entityDescriptor = this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Source); EntityDescriptor descriptor2 = (binding.Target != null) ? this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Target) : null; Uri requestUri = null; if (entityDescriptor.GetLatestIdentity() == null) { if (!this.IsBatch) { binding.ContentGeneratedForSave = true; throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_LinkResourceInsertFailure, entityDescriptor.SaveError); } Uri uri = this.CreateRequestRelativeUri(binding); requestUri = Util.CreateUri("$" + entityDescriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture) + "/" + CommonUtil.UriToString(uri), UriKind.Relative); } else { if ((!this.IsBatch && (descriptor2 != null)) && (descriptor2.GetLatestIdentity() == null)) { binding.ContentGeneratedForSave = true; throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_LinkResourceInsertFailure, descriptor2.SaveError); } requestUri = this.CreateRequestUri(entityDescriptor, binding); } ODataRequestMessageWrapper requestMessage = this.CreateRequestMessage(requestUri, GetLinkHttpMethod(binding)); if (this.IsBatch) { requestMessage.SetHeader("Content-ID", binding.ChangeOrder.ToString(CultureInfo.InvariantCulture)); } if ((EntityStates.Added == binding.State) || ((EntityStates.Modified == binding.State) && (binding.Target != null))) { requestMessage.SetHeader("Content-Type", "application/xml"); } WebUtil.SetOperationVersionHeaders(requestMessage, Util.DataServiceVersion1, this.RequestInfo.MaxProtocolVersionAsVersion); return(requestMessage); }
protected bool CreateChangeData(int index, ODataRequestMessageWrapper requestMessage) { Descriptor descriptor = this.ChangedEntries[index]; if (descriptor.DescriptorKind == DescriptorKind.Entity) { EntityDescriptor entityDescriptor = (EntityDescriptor)descriptor; descriptor.ContentGeneratedForSave = true; return(this.CreateRequestData(entityDescriptor, requestMessage)); } descriptor.ContentGeneratedForSave = true; LinkDescriptor binding = (LinkDescriptor)descriptor; if ((EntityStates.Added != binding.State) && ((EntityStates.Modified != binding.State) || (binding.Target == null))) { return(false); } this.CreateRequestData(binding, requestMessage); return(true); }
internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage) { using (ODataMessageWriter writer = CreateMessageWriter(requestMessage, this.requestInfo)) { Uri resourceUri; EntityDescriptor entityDescriptor = this.requestInfo.EntityTracker.GetEntityDescriptor(binding.Target); if (entityDescriptor.GetLatestIdentity() != null) { resourceUri = entityDescriptor.GetResourceUri(this.requestInfo.BaseUriResolver, false); } else { resourceUri = Util.CreateUri("$" + entityDescriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative); } ODataEntityReferenceLink link = new ODataEntityReferenceLink { Url = resourceUri }; writer.WriteEntityReferenceLink(link); } }
internal void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor linkDescriptor = new LinkDescriptor(source, sourceProperty, target, this.maxProtocolVersion); LinkDescriptor descriptor2 = this.TryGetLinkDescriptor(source, sourceProperty, target); if (descriptor2 != null) { switch (linkMerge) { case MergeOption.OverwriteChanges: linkDescriptor = descriptor2; break; case MergeOption.PreserveChanges: if (((EntityStates.Added == descriptor2.State) || (EntityStates.Unchanged == descriptor2.State)) || ((EntityStates.Modified == descriptor2.State) && (descriptor2.Target != null))) { linkDescriptor = descriptor2; } break; case MergeOption.NoTracking: throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_RelationAlreadyContained); } } else { ClientEdmModel model = ClientEdmModel.GetModel(this.maxProtocolVersion); if (model.GetClientTypeAnnotation(model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection || ((descriptor2 = this.DetachReferenceLink(source, sourceProperty, target, linkMerge)) == null)) { this.AddLink(linkDescriptor); this.IncrementChange(linkDescriptor); } else if ((linkMerge != MergeOption.AppendOnly) && ((MergeOption.PreserveChanges != linkMerge) || (EntityStates.Modified != descriptor2.State))) { linkDescriptor = descriptor2; } } linkDescriptor.State = EntityStates.Unchanged; }
/// <summary> /// find and detach link for reference property /// </summary> /// <param name="source">source entity</param> /// <param name="sourceProperty">source entity property name for target entity</param> /// <param name="target">target entity</param> /// <param name="linkMerge">link merge option</param> /// <returns>true if found and not removed</returns> internal LinkDescriptor DetachReferenceLink(object source, string sourceProperty, object target, MergeOption linkMerge) { Debug.Assert(sourceProperty.IndexOf('/') == -1, "sourceProperty.IndexOf('/') == -1"); LinkDescriptor existing = this.GetLinks(source, sourceProperty).FirstOrDefault(); if (null != existing) { if ((target == existing.Target) || (MergeOption.AppendOnly == linkMerge) || (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State)) { return(existing); } // Since we don't support deep insert on reference property, no need to check for deep insert. this.DetachExistingLink(existing, false); Debug.Assert(!this.Links.Any(o => (o.Source == source) && (o.SourceProperty == sourceProperty)), "only expecting one"); } return(null); }
/// <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() { Debug.Assert( this.mergeOption != MergeOption.OverwriteChanges || this.foundEntriesWithMedia.Count == 0, "mergeOption != MergeOption.OverwriteChanges || foundEntriesWithMedia.Count == 0 - we only use the 'entries-with-media' lookaside when we're not in overwrite mode, otherwise we track everything through identity stack"); if (!this.Tracking) { return; } foreach (KeyValuePair <String, AtomEntry> entity in this.identityStack) { AtomEntry entry = entity.Value; if (entry.CreatedByMaterializer || entry.ResolvedObject == this.insertRefreshObject || entry.ShouldUpdateFromPayload) { // Create a new descriptor and try to attach, if one already exists, get the existing reference instead. EntityDescriptor descriptor = new EntityDescriptor(entity.Key, entry.QueryLink, entry.EditLink, entry.ResolvedObject, null, null, null, entry.ETagText, EntityStates.Unchanged); descriptor = this.context.InternalAttachEntityDescriptor(descriptor, false); // we should always reset descriptor's state to Unchanged (old v1 behaviour) descriptor.State = EntityStates.Unchanged; this.ApplyMediaEntryInformation(entry, descriptor); descriptor.ServerTypeName = entry.TypeName; } else { // Refresh the entity state indirectly by calling TryGetEntity. EntityStates state; this.context.TryGetEntity(entity.Key, entry.ETagText, this.mergeOption, out state); } } // Regardless of the merge mode, media link information should // always be applied to the context. foreach (AtomEntry entry in this.foundEntriesWithMedia.Values) { Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise it wasn't found"); EntityDescriptor descriptor = this.context.GetEntityDescriptor(entry.ResolvedObject); this.ApplyMediaEntryInformation(entry, descriptor); } foreach (LinkDescriptor link in this.links) { if (EntityStates.Added == link.State) { // Added implies collection if ((EntityStates.Deleted == this.context.GetEntityDescriptor(link.Target).State) || (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State)) { this.context.DeleteLink(link.Source, link.SourceProperty, link.Target); } else { this.context.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) { LinkDescriptor end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault(); if (null != end && null == end.Target) { // leave the SetLink(link.Source, link.SourceProperty, null) continue; } if ((null != target) && (EntityStates.Deleted == this.context.GetEntityDescriptor(target).State) || (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State)) { target = null; } } this.context.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption); } else { // detach link Debug.Assert(EntityStates.Detached == link.State, "not detached link"); this.context.DetachLink(link.Source, link.SourceProperty, link.Target); } } }
/// <summary> /// Remove the link from the list of tracked link descriptors. /// </summary> /// <param name="linkDescriptor">link to be removed.</param> /// <returns>true if the link was tracked and now removed, otherwise returns false.</returns> internal bool TryRemoveLinkDescriptor(LinkDescriptor linkDescriptor) { this.EnsureLinkBindings(); return(this.bindings.Remove(linkDescriptor)); }