/// <summary> /// Tracks a call to GetReadStream /// </summary> /// <param name="data">The context data</param> /// <param name="entity">The entity</param> /// <param name="name">The name of the stream or null to indicate the default stream</param> /// <param name="contentType">The content type from the returned stream response</param> /// <param name="headers">The headers from the returned stream response</param> public static void TrackGetReadStream(this DataServiceContextData data, object entity, string name, string contentType, IDictionary <string, string> headers) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); ExceptionUtilities.CheckArgumentNotNull(headers, "headers"); EntityDescriptorData descriptorData; ExceptionUtilities.Assert(data.TryGetEntityDescriptorData(entity, out descriptorData), "Cannot set save stream on an entity that is not being tracked"); if (name == null) { string etag; if (headers.TryGetValue(HttpHeaders.ETag, out etag)) { descriptorData.StreamETag = etag; } } else { var streamDescriptor = descriptorData.StreamDescriptors.SingleOrDefault(n => n.Name == name); ExceptionUtilities.CheckObjectNotNull(streamDescriptor, "Could not find stream descriptor with name '{0}' on entity descriptor: {1}", name, descriptorData); streamDescriptor.ContentType = contentType; streamDescriptor.UpdateFromHeaders(headers); } }
private HttpRequestData CreateInsertRequest(DataServiceContextData dataBeforeSaveChanges, EntityDescriptorData entityDescriptorData) { Uri insertUri; if (entityDescriptorData.InsertLink != null) { insertUri = entityDescriptorData.InsertLink; } else { ExceptionUtilities.CheckObjectNotNull(entityDescriptorData.ParentForInsert, "Entity descriptor data did not have insert link or parent for insert: {0}", entityDescriptorData); ExceptionUtilities.CheckObjectNotNull(entityDescriptorData.ParentPropertyForInsert, "Entity descriptor data did not have insert link or parent property for insert: {0}", entityDescriptorData); var parentDescriptor = dataBeforeSaveChanges.GetEntityDescriptorData(entityDescriptorData.ParentForInsert); var linkInfo = parentDescriptor.LinkInfos.SingleOrDefault(l => l.Name == entityDescriptorData.ParentPropertyForInsert); if (linkInfo != null && linkInfo.NavigationLink != null) { insertUri = linkInfo.NavigationLink; } else { insertUri = new Uri(UriHelpers.ConcatenateUriSegments(parentDescriptor.EditLink.OriginalString, entityDescriptorData.ParentPropertyForInsert)); } } return(new HttpRequestData() { Verb = HttpVerb.Post, Uri = insertUri }); }
internal static Uri GetEntityInsertUri(DataServiceContextData contextData, EntityDescriptorData entityDescriptorData) { Uri insertUri; if (entityDescriptorData.InsertLink != null) { insertUri = entityDescriptorData.InsertLink; } else { ExceptionUtilities.CheckObjectNotNull(entityDescriptorData.ParentForInsert, "Entity descriptor data did not have insert link or parent for insert: {0}", entityDescriptorData); ExceptionUtilities.CheckObjectNotNull(entityDescriptorData.ParentPropertyForInsert, "Entity descriptor data did not have insert link or parent property for insert: {0}", entityDescriptorData); var parentDescriptor = contextData.GetEntityDescriptorData(entityDescriptorData.ParentForInsert); var linkInfo = parentDescriptor.LinkInfos.SingleOrDefault(l => l.Name == entityDescriptorData.ParentPropertyForInsert); if (linkInfo != null && linkInfo.NavigationLink != null) { insertUri = linkInfo.NavigationLink; } else { insertUri = new Uri(UriHelpers.ConcatenateUriSegments(parentDescriptor.EditLink.OriginalString, entityDescriptorData.ParentPropertyForInsert), UriKind.RelativeOrAbsolute); if (!insertUri.IsAbsoluteUri && contextData.BaseUri != null) { insertUri = new Uri(contextData.BaseUri, insertUri); } } } return(insertUri); }
/// <summary> /// Tracks the DeleteLink 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 TrackDeleteLink(this DataServiceContextData data, object source, string sourcePropertyName, object target) { EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot delete link:", "source"); EntityDescriptorData targetDescriptorData = GetTrackedEntityDescriptorData(data, target, "Cannot delete link:", "target"); LinkDescriptorData descriptorData; if (data.TryGetLinkDescriptorData(sourceDescriptorData, sourcePropertyName, targetDescriptorData, out descriptorData) && descriptorData.State == EntityStates.Added) { data.RemoveDescriptorData(descriptorData); } else { CheckStateIsNot(EntityStates.Added, sourceDescriptorData, "Cannot delete link:", "source"); CheckStateIsNot(EntityStates.Added, targetDescriptorData, "Cannot delete link:", "target"); if (descriptorData == null) { data.CreateLinkDescriptorData(EntityStates.Deleted, data.GetNextChangeOrder(), sourceDescriptorData, sourcePropertyName, targetDescriptorData); } else if (descriptorData.State != EntityStates.Deleted) { data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Deleted, data.GetNextChangeOrder()); } } }
/// <summary> /// Tracks the AttachLink 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 TrackAttachLink(this DataServiceContextData data, object source, string sourcePropertyName, object target) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); LinkDescriptorData linkDescriptorData; if (data.TryGetLinkDescriptorData(source, sourcePropertyName, target, out linkDescriptorData)) { throw new TaupoInvalidOperationException( string.Format(CultureInfo.InvariantCulture, "The link already exists: {0}.", linkDescriptorData.ToString())); } EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot attach link:", "source"); CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot attach link:", "source"); CheckStateIsNot(EntityStates.Added, sourceDescriptorData, "Cannot attach link:", "source"); EntityDescriptorData targetDescriptorData = null; if (target != null) { targetDescriptorData = GetTrackedEntityDescriptorData(data, target, "Cannot attach link:", "target"); CheckStateIsNot(EntityStates.Deleted, targetDescriptorData, "Cannot attach link:", "target"); CheckStateIsNot(EntityStates.Added, targetDescriptorData, "Cannot attach link:", "target"); } data.CreateLinkDescriptorData(EntityStates.Unchanged, data.GetNextChangeOrder(), sourceDescriptorData, sourcePropertyName, targetDescriptorData); }
/// <summary> /// Tracks the Detach method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="entity">The entity.</param> /// <returns>Returns the expected result of the Detach method call.</returns> public static bool TrackDetach(this DataServiceContextData data, object entity) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); EntityDescriptorData entityDescriptorData; if (data.TryGetEntityDescriptorData(entity, out entityDescriptorData)) { if (data.EntityDescriptorsData.Any(ed => ed.ParentForInsert == entity && !object.ReferenceEquals(ed, entityDescriptorData))) { throw new TaupoInvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Cannot detach entity as it's used as a parent for insert. Entity descriptor data: {0}.", entityDescriptorData)); } foreach (LinkDescriptorData link in data.LinkDescriptorsData .Where(l => l.SourceDescriptor == entityDescriptorData || l.TargetDescriptor == entityDescriptorData).ToList()) { data.RemoveDescriptorData(link); } return(data.RemoveDescriptorData(entityDescriptorData)); } return(false); }
private ExpectedClientRequest CreateEntityInsertRequest(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { ExceptionUtilities.Assert(!entityDescriptorData.IsMediaLinkEntry, "Can only be used for non media-link-entries"); var insertUri = GetEntityInsertUri(contextData, entityDescriptorData); ExpectedClientRequest request = new ExpectedClientRequest() { Verb = HttpVerb.Post, Uri = insertUri }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); DataServiceProtocolVersion dsv = GetDataServiceVersion(HttpVerb.Post, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); var payload = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); request.Body = payload; this.AddFoldedLinksToEntityInsertPayload(contextData, entityDescriptorData, payload); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); string hintString = @"Entity insert\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return(request); }
private void AddFoldedLinksToEntityInsertPayload(DataServiceContextData contextData, EntityDescriptorData entityDescriptorData, EntityInstance payload) { var entityType = this.ModelSchema.EntityTypes.Single(t => t.FullName == entityDescriptorData.EntityClrType.FullName); foreach (var linkDescriptor in contextData.LinkDescriptorsData.Where(l => l.SourceDescriptor == entityDescriptorData)) { if (linkDescriptor.TargetDescriptor.State == EntityStates.Added) { continue; } var navigationProperty = entityType.AllNavigationProperties.Single(n => n.Name == linkDescriptor.SourcePropertyName); string contentType = MimeTypes.ApplicationAtomXml + ";type="; if (navigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many) { contentType += "feed"; } else { contentType += "entry"; } // 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 var link = new DeferredLink() { UriString = linkDescriptor.TargetDescriptor.EditLink.OriginalString } .WithContentType(contentType).WithTitleAttribute(linkDescriptor.SourcePropertyName); payload.Add(new NavigationPropertyInstance(linkDescriptor.SourcePropertyName, link)); } }
private ExpectedClientRequest CreateEntityUpdateRequest(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { var request = new ExpectedClientRequest() { Verb = GetUpdateVerb(options), Uri = entityDescriptorData.EditLink, }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); var dsv = GetDataServiceVersion(request.Verb, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); request.Body = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); string hintString = @"Entity update\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return(request); }
/// <summary> /// Wraps the specified product. If product is a DataServiceContext then registeres it for tracking. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="product">The product.</param> /// <returns>The wrapper for the product instance.</returns> public TResult Wrap <TResult>(object product) where TResult : IWrappedObject { if (product == null) { return(default(TResult)); } Type[] argTypes = new Type[] { typeof(IWrapperScope), typeof(object) }; Type resultType = typeof(TResult); ConstructorInfo ctor = resultType.GetInstanceConstructor(true, argTypes); ExceptionUtilities.CheckObjectNotNull(ctor, "Cannot find constructor: {0}(IWrapperScope, object)", typeof(TResult).Name); DSClient.DataServiceContext context = product as DSClient.DataServiceContext; if (context != null) { DataServiceProtocolVersion maxProtocolVersion = DataServiceProtocolVersion.Unspecified; #if !WINDOWS_PHONE maxProtocolVersion = context.MaxProtocolVersion.ToTestEnum(); #endif var contextData = DataServiceContextData.CreateDataServiceContextDataFromDataServiceContext(context, maxProtocolVersion); this.contextDatas.Add(context, contextData); } return((TResult)ctor.Invoke(new object[] { this, product })); }
/// <summary> /// Initializes a new instance of the PipelineEmulator class /// </summary> /// <param name="parent">The parent instance to get dependencies from</param> /// <param name="contextData">The current context data</param> /// <param name="propertyValuesBeforeSave">The property values of the tracked client objects before the call to SaveChanges</param> /// <param name="options">The current save-changes options</param> /// <param name="requestResponsePairs">The observed http traffic for the current save-changes call</param> public PipelineEmulator(SaveChangesHttpValidatingEmulator parent, DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, SaveChangesOptions options, IEnumerable <KeyValuePair <IHttpRequest, HttpResponseData> > requestResponsePairs) { this.parent = parent; this.contextData = contextData; this.propertyValuesBeforeSave = propertyValuesBeforeSave; this.options = options; this.httpQueue = new Queue <KeyValuePair <IHttpRequest, HttpResponseData> >(requestResponsePairs); }
/// <summary> /// Verifies the data service context. /// </summary> /// <param name="contextData">The expected data for the context.</param> /// <param name="context">The context to verify.</param> public void VerifyDataServiceContext(DataServiceContextData contextData, DataServiceContext context) { ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); var verifier = new Verifier(contextData, context); verifier.Verify(); }
/// <summary> /// Tracks the UpdateObject method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="entity">The entity.</param> public static void TrackUpdateObject(this DataServiceContextData data, object entity) { var descriptorData = GetTrackedEntityDescriptorData(data, entity, "Failed to track UpdateObject:", "entity"); if (descriptorData.State == EntityStates.Unchanged) { data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Modified, data.GetNextChangeOrder()); } }
/// <summary> /// Validates the requests sent, updates the expected state, and produces the expected response data for a single call to SaveChanges /// </summary> /// <param name="contextData">The context data at the time save-changes was called</param> /// <param name="propertyValuesBeforeSave">The property values of the tracked client objects before the call to SaveChanges</param> /// <param name="options">The save changes options used</param> /// <param name="requestResponsePairs">The observed HTTP traffic during save-changes</param> /// <returns>The expected response data</returns> public DataServiceResponseData ValidateAndTrackChanges(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, SaveChangesOptions options, IEnumerable <KeyValuePair <HttpRequestData, HttpResponseData> > requestResponsePairs) { ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(requestResponsePairs, "requestResponsePairs"); var castPairs = requestResponsePairs.Select(p => new KeyValuePair <IHttpRequest, HttpResponseData>(p.Key, p.Value)); var emulator = new PipelineEmulator(this, contextData, propertyValuesBeforeSave, options, castPairs); return(emulator.Run()); }
/// <summary> /// Tracks the AttachTo method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="entitySetName">Name of the entity set.</param> /// <param name="entity">The entity.</param> /// <param name="identity">The identity.</param> /// <param name="entityETag">The etag for the entity.</param> /// <param name="editLink">The edit link for the entity</param> public static void TrackAttachTo(this DataServiceContextData data, string entitySetName, object entity, Uri identity, string entityETag, Uri editLink) { CheckEntitySetName(ref entitySetName); CheckEntityIsNotTracked(data, entity); CheckIdentity(data, identity); data.CreateEntityDescriptorData(EntityStates.Unchanged, data.GetNextChangeOrder(), entity) .SetEntitySetName(entitySetName) .SetETag(entityETag) .SetIdentity(identity) .SetEditLink(editLink); }
private static void CheckEntityIsNotTracked(DataServiceContextData data, object entity) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); EntityDescriptorData descriptorData; if (data.TryGetEntityDescriptorData(entity, out descriptorData)) { throw new TaupoInvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Specified entity is already tracked by the descriptor data: {0}.", descriptorData.ToString())); } }
private void ApplyNextResponseHeadersAndPayload(DataServiceContextData dataBeforeSaveChanges, EntityDescriptorData entityDescriptorData, Queue <DSClient.OperationResponse> responseQueue) { ExceptionUtilities.CheckArgumentNotNull(dataBeforeSaveChanges, "dataBeforeSaveChanges"); ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData"); ExceptionUtilities.CheckArgumentNotNull(responseQueue, "responseQueue"); ExceptionUtilities.Assert(responseQueue.Count > 0, "Response queue unexpectedly empty"); var responseForRequest = responseQueue.Dequeue(); entityDescriptorData.UpdateFromHeaders(responseForRequest.Headers); }
/// <summary> /// Tracks the DeleteObject method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="entity">The entity.</param> public static void TrackDeleteObject(this DataServiceContextData data, object entity) { var descriptorData = GetTrackedEntityDescriptorData(data, entity, "Failed to track DeleteObject:", "entity"); if (descriptorData.State == EntityStates.Added) { data.RemoveDescriptorData(descriptorData); } else if (descriptorData.State != EntityStates.Deleted) { data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Deleted, data.GetNextChangeOrder()); } }
private static EntityDescriptorData GetTrackedEntityDescriptorData(DataServiceContextData data, object entity, string errorMessage, string argumentName) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); EntityDescriptorData descriptorData; if (!data.TryGetEntityDescriptorData(entity, out descriptorData)) { throw new TaupoInvalidOperationException(errorMessage + " " + argumentName + " is not tracked by the data service context data."); } return(descriptorData); }
private void TrackSetSaveStream(DataServiceContextData data, MethodBase methodToTrack, object[] parameterValues) { string name; int streamParameterIndex; bool closeStream; IEnumerable <KeyValuePair <string, string> > headers; // if the second arg is not a string, then it is an MR, in which case null is correct if (methodToTrack.GetParameters()[1].Name == "name") { name = (string)parameterValues[1]; streamParameterIndex = 2; closeStream = (bool)parameterValues[3]; var args = parameterValues[4] as DSClient.DataServiceRequestArgs; if (args == null) { headers = new HttpHeaderCollection() { ContentType = (string)parameterValues[4] }; } else { headers = args.Headers; } } else { name = null; streamParameterIndex = 1; closeStream = (bool)parameterValues[2]; var args = parameterValues[3] as DSClient.DataServiceRequestArgs; if (args == null) { headers = new HttpHeaderCollection() { ContentType = (string)parameterValues[3], Slug = (string)parameterValues[4] }; } else { headers = args.Headers; } } var streamProxy = parameterValues[streamParameterIndex] as IStreamLogger; ExceptionUtilities.CheckObjectNotNull(streamProxy, "Stream was not wrapped by a stream logger"); data.TrackSetSaveStream(parameterValues[0], name, streamProxy, closeStream, headers); }
private static HttpRequestData BuildBatchRequest(DataServiceContextData contextDataClone) { ExceptionUtilities.CheckObjectNotNull(contextDataClone.BaseUri, "Base uri cannot be null in batch cases"); var batchRequest = new HttpRequestData() { Verb = HttpVerb.Post, Uri = new Uri(UriHelpers.ConcatenateUriSegments(contextDataClone.BaseUri.OriginalString, Endpoints.Batch)) }; SetExpectedIfMatchHeader(batchRequest, null); SetUnexpectedPreferHeader(batchRequest); SetExpectedXHTTPMethodHeader(batchRequest, false); return(batchRequest); }
/// <summary> /// Verifies that the values on the server are correct /// </summary> /// <param name="continuation">The async continuation</param> /// <param name="contextData">The context data</param> public void VerifyChangesOnServer(IAsyncContinuation continuation, DataServiceContextData contextData) { ExceptionUtilities.CheckArgumentNotNull(continuation, "continuation"); ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckAllRequiredDependencies(this); this.currentContextData = contextData; IEnumerable <ChangeData> expectedChanges; ExceptionUtilities.Assert(this.expectedChangeCache.TryGetValue(contextData, out expectedChanges), "Expected changes for given context have not been initialized"); this.expectedChangeCache.Remove(contextData); AsyncHelpers.AsyncForEach(expectedChanges, continuation, this.VerifyChangeOnServer); }
/// <summary> /// Tracks the AddRelatedObject 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 TrackAddRelatedObject(this DataServiceContextData data, object source, string sourcePropertyName, object target) { CheckEntityIsNotTracked(data, target); EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot add link:", "source"); CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot add related object:", "source"); EntityDescriptorData targetDescriptorData = data.CreateEntityDescriptorData(EntityStates.Added, data.GetNextChangeOrder(), target); data.CreateLinkDescriptorData(EntityStates.Added, uint.MaxValue, sourceDescriptorData, sourcePropertyName, targetDescriptorData); targetDescriptorData .SetParentForInsert(source) .SetParentPropertyForInsert(sourcePropertyName); }
private EntityInstance BuildEntityPayload( DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, DataServiceProtocolVersion dsv) { IEnumerable <NamedValue> propertyValues; ExceptionUtilities.Assert(propertyValuesBeforeSave.TryGetValue(entityDescriptorData.Entity, out propertyValues), "Could not find property values for descriptor: {0}", entityDescriptorData); var entityType = this.ModelSchema.EntityTypes.Single(t => t.FullName == entityDescriptorData.EntityClrType.FullName); var entityInstance = this.PayloadBuilder.EntityInstance(entityType, propertyValues); new ExpectedPayloadNormalizer(contextData, dsv).Normalize(entityInstance, entityDescriptorData); return(entityInstance); }
/// <summary> /// Tracks the AddObject method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="entitySetName">Name of the entity set.</param> /// <param name="entity">The entity.</param> public static void TrackAddObject(this DataServiceContextData data, string entitySetName, object entity) { CheckEntitySetName(ref entitySetName); CheckEntityIsNotTracked(data, entity); var entityDescriptorData = data.CreateEntityDescriptorData(EntityStates.Added, data.GetNextChangeOrder(), entity) .SetEntitySetName(entitySetName); if (data.ResolveEntitySet != null) { entityDescriptorData.InsertLink = data.ResolveEntitySet(entitySetName); } else { entityDescriptorData.InsertLink = new Uri(UriHelpers.ConcatenateUriSegments(data.BaseUri.OriginalString, entitySetName)); } }
private static void CheckIdentity(DataServiceContextData data, Uri identity) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); if (identity == null) { throw new TaupoInvalidOperationException("Entity identity cannot be null or empty."); } EntityDescriptorData descriptorData; if (data.TryGetEntityDescriptorData(identity, out descriptorData)) { throw new TaupoInvalidOperationException( string.Format(CultureInfo.InvariantCulture, "There is already an entity descriptor data with the specified identity: {0}.", descriptorData.ToString())); } }
/// <summary> /// Calculates an entity id based on the base uri, entity set resolver, set name, and entity key values /// </summary> /// <param name="contextData">The context data</param> /// <param name="entitySetName">the entity set name</param> /// <param name="entity">The entity to generate an id for</param> /// <returns>The id for the given entity</returns> public Uri CalculateEntityId(DataServiceContextData contextData, string entitySetName, object entity) { ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(entitySetName, "entitySetName"); ExceptionUtilities.CheckArgumentNotNull(entity, "entity"); if (contextData.BaseUri != null) { ExceptionUtilities.Assert(contextData.BaseUri.IsAbsoluteUri, "Base uri must be absolute. Uri was: '{0}'", contextData.BaseUri.OriginalString); return(new Uri(UriHelpers.ConcatenateUriSegments(contextData.BaseUri.AbsoluteUri, Uri.EscapeDataString(entitySetName) + this.CalculateEntityKey(entity)))); } else { ExceptionUtilities.CheckObjectNotNull(contextData.ResolveEntitySet, "Entity set resolver cannot be null if base uri is null"); var resolvedUri = contextData.ResolveEntitySet(entitySetName); ExceptionUtilities.CheckObjectNotNull(resolvedUri, "Entity set resolver returned null for set name '{0}'", entitySetName); return(new Uri(resolvedUri.AbsoluteUri + this.CalculateEntityKey(entity))); } }
/// <summary> /// Tracks a call to SetSaveStream /// </summary> /// <param name="data">The context data.</param> /// <param name="entity">The entity.</param> /// <param name="name">The name of the stream or null to indicate the default stream.</param> /// <param name="saveStreamLogger">A logger for the save stream</param> /// <param name="closeStream">A value indicating whether or not to close the stream</param> /// <param name="headers">The headers for the request</param> public static void TrackSetSaveStream(this DataServiceContextData data, object entity, string name, IStreamLogger saveStreamLogger, bool closeStream, IEnumerable <KeyValuePair <string, string> > headers) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); ExceptionUtilities.CheckArgumentNotNull(saveStreamLogger, "saveStreamLogger"); ExceptionUtilities.CheckArgumentNotNull(headers, "headers"); EntityDescriptorData descriptorData; ExceptionUtilities.Assert(data.TryGetEntityDescriptorData(entity, out descriptorData), "Cannot set save stream on an entity that is not being tracked"); StreamDescriptorData streamDescriptor; if (name == null) { if (descriptorData.State == EntityStates.Added) { descriptorData.DefaultStreamState = EntityStates.Added; } else { descriptorData.DefaultStreamState = EntityStates.Modified; } streamDescriptor = descriptorData.DefaultStreamDescriptor; } else { streamDescriptor = descriptorData.StreamDescriptors.SingleOrDefault(n => n.Name == name); if (streamDescriptor == null) { streamDescriptor = descriptorData.CreateStreamDescriptorData(EntityStates.Modified, data.GetNextChangeOrder(), name); } else { data.ChangeStateAndChangeOrder(streamDescriptor, EntityStates.Modified, data.GetNextChangeOrder()); } } // we need to trap what the product reads from the stream to compare it against what is sent on the wire streamDescriptor.SaveStream = new SaveStreamData(saveStreamLogger, closeStream, headers); }
/// <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> /// Executes SaveChanges method on the specified context and verifies the results. /// </summary> /// <param name="continuation">The async continuation</param> /// <param name="contextData">The context data.</param> /// <param name="context">The context to verify SaveChanges.</param> /// <param name="options">The options for saving changes. Passing null will use the context's default.</param> /// <param name="onCompletion">callback for when save changes verification completes</param> public virtual void VerifySaveChanges(IAsyncContinuation continuation, DataServiceContextData contextData, DSClient.DataServiceContext context, SaveChangesOptions?options, Action <IAsyncContinuation, DSClient.DataServiceResponse> onCompletion) { ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); ExceptionUtilities.CheckArgumentNotNull(onCompletion, "onCompletion"); ExceptionUtilities.CheckAllRequiredDependencies(this); // ensure that two calls are not being processed in parallel ExceptionUtilities.Assert(this.Input == null, "Input was not null, possibly due to simultaneous calls or using the wrong async continuation"); ExceptionUtilities.Assert(this.State == null, "State was not null, possibly due to simultaneous calls or using the wrong async continuation"); continuation = continuation.OnContinueOrFail( () => { this.Input = null; this.State = null; }); this.Input = new VerifierInput() { ContextData = contextData, Context = context, Options = options, OnCompletion = onCompletion, }; AsyncHelpers.RunActionSequence( continuation, this.InitializeState, this.VerifyContextState, this.CallProductApi, this.VerifyRequests, this.VerifyResponse, this.VerifyContextState, this.VerifyServerState, this.Complete); }