/// <summary> /// Gets the URI for requery. /// </summary> /// <returns>Uri for requery.</returns> public override Uri GetUriForRequery() { LinkDescriptorData linkData = this.DescriptorData; Uri uri = null; var linkInfo = linkData.SourceDescriptor.LinkInfos.SingleOrDefault(l => l.Name == linkData.SourcePropertyName); if (linkInfo != null) { uri = linkInfo.NavigationLink; } if (uri == null) { uri = new Uri(linkData.SourceDescriptor.EditLink + "/" + linkData.SourcePropertyName); } if (!this.IsReference) { ExceptionUtilities.Assert(linkData.TargetDescriptor != null, "TargetDescriptor can not be null for the link descriptor representing collection."); ExceptionUtilities.CheckObjectNotNull(linkData.TargetDescriptor.EditLink, "Target edit-link can not be null for the link descriptor representing collection."); Regex keyRegex = new Regex(@".+(?<key>\(.+\))"); Match match = keyRegex.Match(linkData.TargetDescriptor.EditLink.OriginalString); ExceptionUtilities.Assert(match.Success, "Unhandled entity edit-link format. Edit link: " + linkData.TargetDescriptor.EditLink); string targetKey = match.Groups["key"].Value; uri = new Uri(uri + targetKey); } return(uri); }
private HttpRequestData CreateAddLinkRequest(LinkDescriptorData linkDescriptorData, LinkInfoData info) { return(new HttpRequestData() { Uri = this.GetLinkUri(linkDescriptorData, info), Verb = HttpVerb.Post }); }
private HttpRequestData CreateDeleteLinkRequest(LinkDescriptorData linkDescriptorData, LinkInfoData info) { string keyString = this.EntityDescriptorValueCalculator.CalculateEntityKey(linkDescriptorData.TargetDescriptor.Entity); return(new HttpRequestData() { Uri = new Uri(this.GetLinkUri(linkDescriptorData, info).OriginalString + keyString), Verb = HttpVerb.Delete }); }
private void VerifyDescriptor(DescriptorData expected, Descriptor actual, int responseOrder) { EntityDescriptorData entityDescriptorData = expected as EntityDescriptorData; LinkDescriptorData linkDescriptorData = expected as LinkDescriptorData; StreamDescriptorData streamDescriptorData = expected as StreamDescriptorData; if (entityDescriptorData != null) { EntityDescriptor entityDescriptor = actual as EntityDescriptor; this.Assert.IsNotNull(entityDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(EntityDescriptor).Name, actual.GetType().Name, entityDescriptorData)); this.Assert.AreSame( entityDescriptorData.Entity, entityDescriptor.Entity, GetVerificationFailureMessage(responseOrder, "Entity verification failed for the entity descriptor data: {0}.", expected)); } else if (linkDescriptorData != null) { LinkDescriptor linkDescriptor = actual as LinkDescriptor; this.Assert.IsNotNull(linkDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(LinkDescriptor).Name, actual.GetType().Name, linkDescriptorData)); bool notMatch = linkDescriptorData.SourceDescriptor.Entity != linkDescriptor.Source || (linkDescriptorData.TargetDescriptor == null && linkDescriptor.Target != null) || (linkDescriptorData.TargetDescriptor != null && linkDescriptorData.TargetDescriptor.Entity != linkDescriptor.Target) || linkDescriptorData.SourcePropertyName != linkDescriptor.SourceProperty; this.Assert.IsFalse(notMatch, GetVerificationFailureMessage(responseOrder, "Link verification failed.\r\nExpected: {0}\r\nActual: {1}", linkDescriptorData, linkDescriptor.ToTraceString())); } else { #if WINDOWS_PHONE throw new TaupoNotSupportedException("StreamDescriptors are not supported on Windows Phone"); #else ExceptionUtilities.CheckObjectNotNull(streamDescriptorData, "Expected was not an entity, link, or stream descriptor: {0}", expected); StreamDescriptor streamDescriptor = actual as StreamDescriptor; this.Assert.IsNotNull(streamDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(StreamDescriptor).Name, actual.GetType().Name, streamDescriptorData)); this.Assert.AreEqual(streamDescriptorData.State.ToProductEnum(), streamDescriptor.State, GetVerificationFailureMessage(responseOrder, "Stream descriptor state verification failed.")); this.Assert.AreEqual(streamDescriptorData.Name, streamDescriptor.StreamLink.Name, GetVerificationFailureMessage(responseOrder, "Stream descriptor name verification failed.")); this.Assert.AreEqual(streamDescriptorData.ETag, streamDescriptor.StreamLink.ETag, GetVerificationFailureMessage(responseOrder, "Stream descriptor etag verification failed.")); this.Assert.AreEqual(streamDescriptorData.ContentType, streamDescriptor.StreamLink.ContentType, GetVerificationFailureMessage(responseOrder, "Stream descriptor content type verification failed.")); this.Assert.AreEqual(streamDescriptorData.EditLink, streamDescriptor.StreamLink.EditLink, GetVerificationFailureMessage(responseOrder, "Stream descriptor edit link verification failed.")); this.Assert.AreEqual(streamDescriptorData.SelfLink, streamDescriptor.StreamLink.SelfLink, GetVerificationFailureMessage(responseOrder, "Stream descriptor self link verification failed.")); #endif } }
internal static Uri BuildLinkUri(LinkDescriptorData linkDescriptorData, LinkInfoData info) { Uri linkUri; if (info != null && info.RelationshipLink != null) { linkUri = info.RelationshipLink; } else { ExceptionUtilities.CheckObjectNotNull(linkDescriptorData.SourceDescriptor.EditLink, "Edit link of source descriptor cannot be null"); linkUri = new Uri(UriHelpers.ConcatenateUriSegments(linkDescriptorData.SourceDescriptor.EditLink.OriginalString, Endpoints.Ref, linkDescriptorData.SourcePropertyName), UriKind.RelativeOrAbsolute); } return(linkUri); }
private HttpRequestData CreateSetLinkRequest(LinkDescriptorData linkDescriptorData, LinkInfoData info) { HttpVerb verb; if (linkDescriptorData.TargetDescriptor == null) { verb = HttpVerb.Delete; } else { verb = HttpVerb.Put; } return(new HttpRequestData() { Uri = this.GetLinkUri(linkDescriptorData, info), Verb = verb }); }
private LinkDescriptor GetLinkDescriptor(LinkDescriptorData linkDescriptorData) { object sourceEntity = this.GetEntityDescriptor(linkDescriptorData.SourceDescriptor).Entity; object targetEntity = linkDescriptorData.TargetDescriptor != null? this.GetEntityDescriptor(linkDescriptorData.TargetDescriptor).Entity : null; // Note: GetLinkDescriptor throws ArgumenNullException when target is null even so there is an existing link with null target. var descriptor = targetEntity != null? this.context.GetLinkDescriptor(sourceEntity, linkDescriptorData.SourcePropertyName, targetEntity) : this.context.Links.Where(l => l.Source == sourceEntity && l.SourceProperty == linkDescriptorData.SourcePropertyName && l.Target == null).SingleOrDefault(); if (descriptor == null) { throw new AssertionFailedException( string.Format(CultureInfo.InvariantCulture, "Link descriptor is missing from the data service context. Expected link descriptor data: {{{0}}}.", linkDescriptorData)); } return(descriptor); }
/// <summary> /// Tracks the SetLink method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="source">The source.</param> /// <param name="sourcePropertyName">Name of the source property.</param> /// <param name="target">The target.</param> public static void TrackSetLink(this DataServiceContextData data, object source, string sourcePropertyName, object target) { ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(sourcePropertyName, "sourcePropertyName"); EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot set link:", "source"); CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot set link:", "source"); EntityDescriptorData targetDescriptorData = null; if (target != null) { targetDescriptorData = GetTrackedEntityDescriptorData(data, target, "Cannot set link:", "target"); CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot set link:", "target"); } var relatedToSource = data.LinkDescriptorsData.Where(e => e.SourceDescriptor.Entity == source && e.SourcePropertyName == sourcePropertyName).ToList(); if (relatedToSource.Count > 1) { throw new TaupoInvalidOperationException("Cannot set link: source contains multiple links for the property: " + sourcePropertyName); } LinkDescriptorData existingLinkDescriptorData = relatedToSource.FirstOrDefault(); if (existingLinkDescriptorData == null) { data.CreateLinkDescriptorData(EntityStates.Modified, data.GetNextChangeOrder(), sourceDescriptorData, sourcePropertyName, targetDescriptorData); } else { if (existingLinkDescriptorData.State != EntityStates.Modified || existingLinkDescriptorData.TargetDescriptor != targetDescriptorData) { data.ChangeStateAndChangeOrder(existingLinkDescriptorData, EntityStates.Modified, data.GetNextChangeOrder()); } existingLinkDescriptorData.TargetDescriptor = targetDescriptorData; } }
/// <summary> /// Creates LinkChangeData which captures the state of the specified link descriptor data. /// </summary> /// <param name="linkData">The link descriptor data.</param> /// <returns>Link change data.</returns> public static LinkChangeData Create(LinkDescriptorData linkData) { ExceptionUtilities.CheckArgumentNotNull(linkData, "linkData"); LinkChangeData changeData = new LinkChangeData(linkData); var isEnumerable = linkData.SourceDescriptor.EntityClrType.GetProperty(linkData.SourcePropertyName).PropertyType.GetInterfaces().Any(t => t.Name == "IEnumerable"); changeData.IsReference = !isEnumerable; if (linkData.TargetDescriptor != null) { changeData.ClrTypeForRequery = linkData.TargetDescriptor.EntityClrType; } else { ExceptionUtilities.Assert(changeData.IsReference, "TargetDescriptor can not be null for the link descriptor representing collection."); changeData.ClrTypeForRequery = linkData.SourceDescriptor.EntityClrType.GetProperty(linkData.SourcePropertyName).PropertyType; } return(changeData); }
private ExpectedClientRequest CreateLinkRequest(LinkDescriptorData linkDescriptorData, SaveChangesOptions options) { var info = linkDescriptorData.SourceDescriptor.LinkInfos.SingleOrDefault(l => l.Name == linkDescriptorData.SourcePropertyName); ExpectedClientRequest request = new ExpectedClientRequest() { Uri = BuildLinkUri(linkDescriptorData, info) }; if (linkDescriptorData.State == EntityStates.Added) { request.Verb = HttpVerb.Post; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } else if (linkDescriptorData.State == EntityStates.Modified) { if (linkDescriptorData.TargetDescriptor == null) { request.Verb = HttpVerb.Delete; } else { request.Verb = HttpVerb.Put; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } } else { ExceptionUtilities.Assert(linkDescriptorData.State == EntityStates.Deleted, "Link descriptor was in unexpected state '{0}'", linkDescriptorData.State); string keyString = this.EntityDescriptorValueCalculator.CalculateEntityKey(linkDescriptorData.TargetDescriptor.Entity); request.Uri = new Uri(request.Uri.OriginalString + keyString); request.Verb = HttpVerb.Delete; } request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = null; request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4); this.SetDefaultAcceptHeader(request, options); if (request.Verb != HttpVerb.Delete) { request.Headers[HttpHeaders.ContentType] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? MimeTypes.ApplicationXml : this.ClientRequestAcceptHeader; } else { request.Headers[HttpHeaders.ContentType] = null; } string hintString = @"Link\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, linkDescriptorData, options); return(request); }
/// <summary> /// Tracks the SaveChanges method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="options">The options.</param> /// <param name="response">The response.</param> /// <param name="cachedOperationsFromResponse">The individual operation respones, pre-enumerated and cached.</param> /// <param name="tracker">The entity data change tracker to use</param> public static void TrackSaveChanges(this DataServiceContextData data, SaveChangesOptions options, DSClient.DataServiceResponse response, IEnumerable <DSClient.OperationResponse> cachedOperationsFromResponse, IEntityDescriptorDataChangeTracker tracker) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); ExceptionUtilities.CheckArgumentNotNull(response, "response"); ExceptionUtilities.CheckArgumentNotNull(cachedOperationsFromResponse, "cachedOperationsFromResponse"); ExceptionUtilities.CheckArgumentNotNull(tracker, "tracker"); // Check options and response consistency if ((options & SaveChangesOptions.ContinueOnError) == 0) { ExceptionUtilities.Assert(response.Count(r => r.Error != null) == 0, "Check save changes options and response consistency: no errors in the response when ContinueOnError is off."); } // because some links will not have separate requests, we need to keep track of all link changes var allPendingLinkChanges = data.GetOrderedChanges().OfType <LinkDescriptorData>().ToList(); // go through the pending changes and update the states based on whether the request succeeded foreach (DSClient.ChangeOperationResponse changeResponse in cachedOperationsFromResponse) { DescriptorData descriptorData; LinkDescriptorData linkDescriptorData = null; StreamDescriptorData streamDescriptorData = null; var entityDescriptor = changeResponse.Descriptor as DSClient.EntityDescriptor; if (entityDescriptor != null) { descriptorData = data.GetEntityDescriptorData(entityDescriptor.Entity); } else { var linkDescriptor = changeResponse.Descriptor as DSClient.LinkDescriptor; if (linkDescriptor != null) { linkDescriptorData = data.GetLinkDescriptorData(linkDescriptor.Source, linkDescriptor.SourceProperty, linkDescriptor.Target); descriptorData = linkDescriptorData; allPendingLinkChanges.Remove(linkDescriptorData); } else { // for stream descriptors, we need to find the parent descriptor, then get the stream descriptor data from it var streamDescriptor = (DSClient.StreamDescriptor)changeResponse.Descriptor; entityDescriptor = streamDescriptor.EntityDescriptor; streamDescriptorData = data.GetStreamDescriptorData(entityDescriptor.Entity, streamDescriptor.StreamLink.Name); descriptorData = streamDescriptorData; } } // don't update states for responses that indicate failure if (changeResponse.Error != null) { continue; } // because the request succeeded, make the corresponding updates to the states if (descriptorData.State == EntityStates.Deleted || (linkDescriptorData != null && linkDescriptorData.State == EntityStates.Modified && linkDescriptorData.SourceDescriptor.State == EntityStates.Deleted)) { data.RemoveDescriptorData(descriptorData); } else { // for non-deleted descriptors, we need to update states based on the headers var entityDescriptorData = descriptorData as EntityDescriptorData; if (entityDescriptorData != null) { if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified) { entityDescriptorData.DefaultStreamDescriptor.UpdateFromHeaders(changeResponse.Headers); entityDescriptorData.DefaultStreamState = EntityStates.Unchanged; } else { if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Added) { entityDescriptorData.DefaultStreamState = EntityStates.Unchanged; } // because there might have been a reading-entity event for this entity, we need to apply the headers through the tracker tracker.TrackUpdateFromHeaders(entityDescriptorData, changeResponse.Headers); // ensure that all updates are applied before moving to the next response tracker.ApplyPendingUpdates(entityDescriptorData); entityDescriptorData.ParentForInsert = null; entityDescriptorData.ParentPropertyForInsert = null; entityDescriptorData.InsertLink = null; } } else if (streamDescriptorData != null) { streamDescriptorData.UpdateFromHeaders(changeResponse.Headers); } descriptorData.State = EntityStates.Unchanged; } } // go through each link change that did not have an assocatiated response and update its state foreach (var linkDescriptorData in allPendingLinkChanges.OfType <LinkDescriptorData>()) { if (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified) { linkDescriptorData.State = EntityStates.Unchanged; } } }
/// <summary> /// Initializes a new instance of the <see cref="LinkChangeData"/> class. /// </summary> /// <param name="linkDescriptorData">The link descriptor data.</param> protected LinkChangeData(LinkDescriptorData linkDescriptorData) : base(linkDescriptorData) { }