예제 #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="EntityDescriptorData"/> class.
        /// </summary>
        /// <param name="contextData">The context data which contains this entity descriptor data</param>
        /// <param name="entity">The entity.</param>
        internal EntityDescriptorData(DataServiceContextData contextData, object entity)
            : this(contextData)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");

            this.entity = entity;
            this.EntityClrType = entity.GetType();
        }
예제 #2
0
 /// <summary>
 /// Initializes a new instance of the EntityDescriptorData class
 /// </summary>
 /// <param name="contextData">The context data which contains this entity descriptor data</param>
 private EntityDescriptorData(DataServiceContextData contextData)
 {
     ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
     this.contextData = contextData;
     this.linkInfos = new List<LinkInfoData>();
     this.operationDescriptors = new List<OperationDescriptorData>();
     this.streamDescriptors = new List<StreamDescriptorData>();
 }
예제 #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="EntityDescriptorData"/> class.
        /// </summary>
        /// <param name="contextData">The context data which contains this entity descriptor data</param>
        /// <param name="identity">The entity identity.</param>
        /// <param name="entityClrType">Type of the entity.</param>
        internal EntityDescriptorData(DataServiceContextData contextData, Uri identity, Type entityClrType)
            : this(contextData)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityClrType, "entityClrType");

            this.EntityClrType = entityClrType;
            this.Identity = identity;
        }
예제 #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="EntityDescriptorData"/> class.
        /// </summary>
        /// <param name="contextData">The context data which contains this entity descriptor data</param>
        /// <param name="entity">The entity.</param>
        internal EntityDescriptorData(DataServiceContextData contextData, object entity)
            : this(contextData)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");

            this.entity        = entity;
            this.EntityClrType = entity.GetType();
        }
예제 #5
0
 /// <summary>
 /// Initializes a new instance of the EntityDescriptorData class
 /// </summary>
 /// <param name="contextData">The context data which contains this entity descriptor data</param>
 private EntityDescriptorData(DataServiceContextData contextData)
 {
     ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
     this.contextData          = contextData;
     this.linkInfos            = new List <LinkInfoData>();
     this.operationDescriptors = new List <OperationDescriptorData>();
     this.streamDescriptors    = new List <StreamDescriptorData>();
 }
예제 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="EntityDescriptorData"/> class.
        /// </summary>
        /// <param name="contextData">The context data which contains this entity descriptor data</param>
        /// <param name="identity">The entity identity.</param>
        /// <param name="entityClrType">Type of the entity.</param>
        internal EntityDescriptorData(DataServiceContextData contextData, Uri identity, Type entityClrType)
            : this(contextData)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityClrType, "entityClrType");

            this.EntityClrType = entityClrType;
            this.Identity      = identity;
        }
예제 #7
0
        /// <summary>
        /// Executes SaveChanges on the specified context and with the default options and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="contextData">The data for the context.</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <returns>The response from SaveChanges</returns>
        public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context)
        {
            ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier");
            ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            return verifier.VerifySaveChanges(contextData, context, null);
        }
        /// <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();
        }
예제 #9
0
        /// <summary>
        /// Extension method to either find or create a link descriptor with the given values
        /// </summary>
        /// <param name="contextData">The context data</param>
        /// <param name="sourceDescriptor">The source descriptor</param>
        /// <param name="propertyName">The property name</param>
        /// <param name="targetDescriptor">The target descriptor</param>
        /// <returns>A link descriptor with the given values</returns>
        public static LinkDescriptorData MaterializeLinkDescriptor(this DataServiceContextData contextData, EntityDescriptorData sourceDescriptor, string propertyName, EntityDescriptorData targetDescriptor)
        {
            var existingLink = contextData.LinkDescriptorsData.SingleOrDefault(l => l.SourceDescriptor == sourceDescriptor && l.SourcePropertyName == propertyName && l.TargetDescriptor == targetDescriptor);

            if (existingLink == null)
            {
                existingLink = contextData.CreateLinkDescriptorData(EntityStates.Unchanged, 0, sourceDescriptor, propertyName, targetDescriptor);
            }

            return(existingLink);
        }
예제 #10
0
        /// <summary>
        /// Creates the data service context data from data service context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="maxProtocolVersion">The max protocol version.</param>
        /// <returns>DataServiceContextData based on context</returns>
        public static DataServiceContextData CreateDataServiceContextDataFromDataServiceContext(DSClient.DataServiceContext context, DataServiceProtocolVersion maxProtocolVersion)
        {
            var contextData = new DataServiceContextData(context.GetType(), maxProtocolVersion);

            // set all the properties on the context data to the values from the context
            foreach (var productProperty in typeof(DSClient.DataServiceContext).GetProperties(true, false))
            {
                var testProperty = typeof(DataServiceContextData).GetProperty(productProperty.Name, true, false);
                if (testProperty != null && testProperty.PropertyType.IsAssignableFrom(productProperty.PropertyType))
                {
                    var value = productProperty.GetValue(context, null);
                    testProperty.SetValue(contextData, value, null);
                }
            }

            return contextData;
        }
        /// <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));
            }   
        }
예제 #12
0
        /// <summary>
        /// Clones the current data service context data
        /// </summary>
        /// <returns>A clone of the current data service context data</returns>
        public DataServiceContextData Clone()
        {
            var clone = new DataServiceContextData(this.ContextType, this.MaxProtocolVersion);
            clone.nextChangeOrder = this.nextChangeOrder;

            if (this.BaseUri != null)
            {
                clone.BaseUri = new Uri(this.BaseUri.OriginalString, UriKind.RelativeOrAbsolute);
            }
            
            clone.AddAndUpdateResponsePreference = this.AddAndUpdateResponsePreference;
            clone.MergeOption = this.MergeOption;
            clone.ResolveEntitySet = this.ResolveEntitySet;
            clone.ResolveName = this.ResolveName;
            clone.ResolveType = this.ResolveType;
            clone.UsePostTunneling = this.UsePostTunneling;
            clone.HttpStack = this.HttpStack;

            var mapping = new Dictionary<EntityDescriptorData, EntityDescriptorData>(ReferenceEqualityComparer.Create<EntityDescriptorData>());
            foreach (var entityDescriptor in this.entityDatas)
            {
                var clonedDescriptor = entityDescriptor.Clone(clone);
                mapping[entityDescriptor] = clonedDescriptor;
                clone.entityDatas.Add(clonedDescriptor);
            }

            foreach (var linkDescriptor in this.linkDatas)
            {
                EntityDescriptorData clonedSource = null;
                EntityDescriptorData clonedTarget = null;
                mapping.TryGetValue(linkDescriptor.SourceDescriptor, out clonedSource);
                if (linkDescriptor.TargetDescriptor != null)
                {
                    mapping.TryGetValue(linkDescriptor.TargetDescriptor, out clonedTarget);
                }

                var clonedDescriptor = linkDescriptor.Clone(clonedSource, clonedTarget);
                clone.linkDatas.Add(clonedDescriptor);
            }

            return clone;
        }
예제 #13
0
        /// <summary>
        /// Clones the current entity descriptor data
        /// </summary>
        /// <returns>A clone of the current entity descriptor data</returns>
        /// <param name="clonedContextData">The cloned context data which will contain the cloned entity descriptor data</param>
        public EntityDescriptorData Clone(DataServiceContextData clonedContextData)
        {
            var clone = new EntityDescriptorData(clonedContextData);

            clone.ChangeOrder   = this.ChangeOrder;
            clone.entity        = this.entity;
            clone.EntityClrType = this.EntityClrType;
            clone.EntitySetName = this.EntitySetName;
            clone.ETag          = this.ETag;
            clone.Identity      = this.Identity;
            clone.linkInfos.AddRange(this.linkInfos.Select(l => l.Clone()));
            clone.operationDescriptors.AddRange(this.operationDescriptors.Select(o => o.Clone()));
            clone.streamDescriptors.AddRange(this.streamDescriptors.Select(n => n.Clone(clone)));
            clone.ParentForInsert         = this.ParentForInsert;
            clone.ParentPropertyForInsert = this.ParentPropertyForInsert;
            clone.State = this.State;

            if (this.EditLink != null)
            {
                clone.EditLink = new Uri(this.EditLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.SelfLink != null)
            {
                clone.SelfLink = new Uri(this.SelfLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.InsertLink != null)
            {
                clone.InsertLink = new Uri(this.InsertLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.IsMediaLinkEntry)
            {
                clone.defaultStreamDescriptor = this.defaultStreamDescriptor.Clone(clone);
            }

            return(clone);
        }
        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;
        }
        private ExpectedClientRequest CreateStreamInsertRequest(DataServiceContextData contextData, StreamDescriptorData streamDescriptorData, SaveChangesOptions options)
        {
            ExceptionUtilities.Assert(streamDescriptorData.Name == null, "Can only be used for media-resources");

            var insertUri = GetEntityInsertUri(contextData, streamDescriptorData.EntityDescriptor);

            var request = this.CreateSaveStreamRequest(streamDescriptorData, insertUri);
            
            string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue();
            var dsv = GetDataServiceVersion(request.Verb, preference);

            request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv);
            request.Headers[HttpHeaders.IfMatch] = null;
            request.Headers[HttpHeaders.Prefer] = preference;

            this.SetDefaultAcceptHeader(request, options);
            SetContentTypeHeaderForStream(request, streamDescriptorData);

            string hintString = @"Stream insert\r\n{{\r\n  Descriptor = {0}\r\n\r\n}}";
            request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, streamDescriptorData);

            return request;
        }
예제 #16
0
        /// <summary>
        /// Clones the current entity descriptor data
        /// </summary>
        /// <returns>A clone of the current entity descriptor data</returns>
        /// <param name="clonedContextData">The cloned context data which will contain the cloned entity descriptor data</param>
        public EntityDescriptorData Clone(DataServiceContextData clonedContextData)
        {
            var clone = new EntityDescriptorData(clonedContextData);
            clone.ChangeOrder = this.ChangeOrder;
            clone.entity = this.entity;
            clone.EntityClrType = this.EntityClrType;
            clone.EntitySetName = this.EntitySetName;
            clone.ETag = this.ETag;
            clone.Identity = this.Identity;
            clone.linkInfos.AddRange(this.linkInfos.Select(l => l.Clone()));
            clone.operationDescriptors.AddRange(this.operationDescriptors.Select(o => o.Clone()));
            clone.streamDescriptors.AddRange(this.streamDescriptors.Select(n => n.Clone(clone)));
            clone.ParentForInsert = this.ParentForInsert;
            clone.ParentPropertyForInsert = this.ParentPropertyForInsert;
            clone.State = this.State;

            if (this.EditLink != null)
            {
                clone.EditLink = new Uri(this.EditLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.SelfLink != null)
            {
                clone.SelfLink = new Uri(this.SelfLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.InsertLink != null)
            {
                clone.InsertLink = new Uri(this.InsertLink.OriginalString, UriKind.RelativeOrAbsolute);
            }

            if (this.IsMediaLinkEntry)
            {
                clone.defaultStreamDescriptor = this.defaultStreamDescriptor.Clone(clone);
            }

            return clone;
        }
        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));
            }
        }
예제 #18
0
        /// <summary>
        /// Executes SaveChanges on the specified context and with specified options and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <returns>The response from SaveChanges</returns>
        public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, WrappedDataServiceContext context, SaveChangesOptions options)
        {
            DataServiceContextData data = CheckParametersAndGetDataServiceContextData(verifier, context);

            return(verifier.VerifySaveChanges(data, (DSClient.DataServiceContext)context.Product, options));
        }
예제 #19
0
        /// <summary>
        /// Executes SaveChanges on the specified context and with specified options and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="contextData">The data for the context.</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <returns>The response from SaveChanges</returns>
        public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context, SaveChangesOptions?options)
        {
            ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier");
            ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            DSClient.DataServiceResponse response = null;
            SyncHelpers.ExecuteActionAndWait(c1 => verifier.VerifySaveChanges(c1, contextData, context, options, (c2, r) => { response = r; c2.Continue(); }));
            return(response);
        }
 /// <summary>
 /// Calculates an edit link for 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 edit link for</param>
 /// <returns>The edit link for the given entity</returns>
 public Uri CalculateEditLink(DataServiceContextData contextData, string entitySetName, object entity)
 {
     return this.CalculateEntityId(contextData, entitySetName, entity);
 }
예제 #21
0
 /// <summary>
 /// Returns a value indicating whether the descriptor should be updated based on its state and the context's merge option
 /// </summary>
 /// <param name="contextData">The context data</param>
 /// <param name="descriptorData">The descriptor data</param>
 /// <param name="isNewDescriptor">A value indicating whether the descriptor has just been created</param>
 /// <returns>True if changes should be applied, false otherwise</returns>
 public static bool ShouldApplyChangeToDescriptor(this DataServiceContextData contextData, DescriptorData descriptorData, bool isNewDescriptor)
 {
     return(isNewDescriptor || contextData.MergeOption == MergeOption.OverwriteChanges || (contextData.MergeOption == MergeOption.PreserveChanges && descriptorData.State == EntityStates.Unchanged));
 }
 /// <summary>
 /// Initializes the expected changes to verify after SaveChanges completes
 /// </summary>
 /// <param name="contextData">The context data</param>
 /// <param name="propertyValues">The property values of the tracked client objects</param>
 public void InitializeExpectedChanges(DataServiceContextData contextData, IDictionary<object, IEnumerable<NamedValue>> propertyValues)
 {
     this.expectedChangeCache[contextData] = contextData.GetOrderedChanges().Where(d => !(d is StreamDescriptorData)).Select(d => this.CreateChangeData(d, propertyValues)).ToList();
 }
        /// <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);
        }
        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 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 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()));
            }
        }
        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>
        /// Calculates the expected response from DataServiceContext.SaveChanges based on the context data before saving changes.
        /// Assumes there are no errors in the response.
        /// </summary>
        /// <param name="dataBeforeSaveChanges">The data before saving changes.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <param name="context">The DataServiceContext instance which is calling SaveChanges.</param>
        /// <returns><see cref="DataServiceResponseData"/> that expresses expectations for the response.</returns>
        public DataServiceResponseData CalculateSaveChangesResponseData(DataServiceContextData dataBeforeSaveChanges, SaveChangesOptions options, DSClient.DataServiceContext context)
        {
            ExceptionUtilities.CheckArgumentNotNull(dataBeforeSaveChanges, "dataBeforeSaveChanges");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            DataServiceResponseData responseData = new DataServiceResponseData();

            responseData.IsBatchResponse = options == SaveChangesOptions.Batch;

            bool hasChanges = false;

            // Note: ordering is important as changes should be processed in the order specified by user.
            foreach (DescriptorData descriptorData in dataBeforeSaveChanges.GetOrderedChanges())
            {
                int statusCode = (int)HttpStatusCode.NoContent;

                var entityDescriptorData = descriptorData as EntityDescriptorData;
                if (entityDescriptorData != null)
                {
                    if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified)
                    {
                        responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                    }

                    if (descriptorData.State == EntityStates.Added)
                    {
                        statusCode = GetStatusCodeForInsert(context);

                        if (entityDescriptorData.IsMediaLinkEntry)
                        {
                            responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                            statusCode = GetStatusCodeForUpdate(context);
                        }
                    }
                    else if (descriptorData.State == EntityStates.Modified)
                    {
                        statusCode = GetStatusCodeForUpdate(context);
                    }
                    else if (descriptorData.State != EntityStates.Deleted)
                    {
                        continue;
                    }
                }

                var linkDescriptorData = descriptorData as LinkDescriptorData;
                if (linkDescriptorData != null && (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified))
                {
                    if (!linkDescriptorData.WillTriggerSeparateRequest())
                    {
                        continue;
                    }
                }

                responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                hasChanges = true;
            }

            if (responseData.IsBatchResponse)
            {
                responseData.BatchStatusCode = hasChanges ? (int)HttpStatusCode.Accepted : 0;
            }
            else
            {
                responseData.BatchStatusCode = -1;
            }

            return responseData;
        }
 /// <summary>
 /// Initializes a new instance of the ExpectedPayloadNormalizer class
 /// </summary>
 /// <param name="contextData">The context data</param>
 /// <param name="dsv">The data service version</param>
 public ExpectedPayloadNormalizer(DataServiceContextData contextData, DataServiceProtocolVersion dsv)
 {
     ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
     this.contextData = contextData;
     this.dsv = dsv;
 }
        /// <summary>
        /// Calculates expected data for a request during DataServiceContext.SaveChanges for a particular descriptor.
        /// </summary>
        /// <param name="contextData">The context data</param>
        /// <param name="propertyValuesBeforeSave">The property values of the tracked client objects before the call to SaveChanges</param>
        /// <param name="descriptorData">The descriptor data</param>
        /// <param name="options">The save changes options</param>
        /// <returns>The expected client request</returns>
        public ExpectedClientRequest CalculateRequest(DataServiceContextData contextData, IDictionary<object, IEnumerable<NamedValue>> propertyValuesBeforeSave, DescriptorData descriptorData, SaveChangesOptions options)
        {
            ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
            ExceptionUtilities.CheckArgumentNotNull(descriptorData, "descriptorData");
            ExceptionUtilities.CheckArgumentNotNull(propertyValuesBeforeSave, "propertyValuesBeforeSave");

            var linkDescriptorData = descriptorData as LinkDescriptorData;
            var entityDescriptorData = descriptorData as EntityDescriptorData;
            var streamDescriptorData = descriptorData as StreamDescriptorData;

            ExpectedClientRequest request = null;
            if (linkDescriptorData != null)
            {
                if (linkDescriptorData.WillTriggerSeparateRequest())
                {
                    request = this.CreateLinkRequest(linkDescriptorData, options);
                }
            }
            else if (entityDescriptorData != null)
            {
                if (entityDescriptorData.State == EntityStates.Added)
                {
                    request = this.CreateEntityInsertRequest(contextData, propertyValuesBeforeSave, entityDescriptorData, options);
                }
                else if (entityDescriptorData.State == EntityStates.Modified)
                {
                    request = this.CreateEntityUpdateRequest(contextData, propertyValuesBeforeSave, entityDescriptorData, options);
                }
                else if (entityDescriptorData.State == EntityStates.Deleted)
                {
                    request = this.CreateEntityDeleteRequest(entityDescriptorData, options);
                }
            }
            else if (streamDescriptorData != null)
            {
                if (streamDescriptorData.State == EntityStates.Added)
                {
                    request = this.CreateStreamInsertRequest(contextData, streamDescriptorData, options);
                }
                else if (streamDescriptorData.State == EntityStates.Modified)
                {
                    request = this.CreateStreamUpdateRequest(streamDescriptorData, options);
                }
            }

            if (request != null)
            {
                request.Headers[HttpHeaders.MaxDataServiceVersion] = ToClientHeaderFormat(contextData.MaxProtocolVersion);

                // perform sanity checks
                var missingHeaders = headersThatWillBeGenerated.Where(h => !request.Headers.ContainsKey(h)).ToArray();
                ExceptionUtilities.Assert(missingHeaders.Length == 0, "Generated request was missing headers: {0}", string.Join(", ", missingHeaders));
                ExceptionUtilities.CheckObjectNotNull(request.Uri, "Generated request was missing a Uri");

                // sanity check: Client sends content-type header for delete request
                if (request.GetEffectiveVerb() == HttpVerb.Delete)
                {
                    ExceptionUtilities.Assert(
                        request.Headers[HttpHeaders.ContentType] == null,
                        "Incorrect expectation: client should never send ContentType header for DELETE requests.");
                }
            }

            return request;
        }
예제 #31
0
        /// <summary>
        /// Executes SaveChanges on the specified context and with specified options and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="contextData">The data for the context.</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <returns>The response from SaveChanges</returns>
        public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context, SaveChangesOptions? options)
        {
#if SILVERLIGHT
            throw new TaupoNotSupportedException("Not supported in Silverlight");
#else
            ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier");
            ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            DSClient.DataServiceResponse response = null;
            SyncHelpers.ExecuteActionAndWait(c1 => verifier.VerifySaveChanges(c1, contextData, context, options, (c2, r) => { response = r; c2.Continue(); }));
            return response;
#endif
        }
        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;
        }
예제 #33
0
        /// <summary>
        /// Executes SaveChanges on the specified context and with the default options and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="contextData">The data for the context.</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <returns>The response from SaveChanges</returns>
        public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context)
        {
            ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier");
            ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            return(verifier.VerifySaveChanges(contextData, context, null));
        }
        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;
        }
예제 #35
0
        /// <summary>
        /// Executes SaveChanges on the specified context and verifies the results.
        /// </summary>
        /// <param name="verifier">The verifier to use for verification.</param>
        /// <param name="continuation">The asynchronous continuation</param>
        /// <param name="context">The context to verify SaveChanges on.</param>
        /// <param name="options">The options to use, or null for the default</param>
        /// <param name="onCompletion">The callback to call on completion</param>
        public static void VerifySaveChanges(this ISaveChangesVerifier verifier, IAsyncContinuation continuation, WrappedDataServiceContext context, SaveChangesOptions?options, Action <IAsyncContinuation, DSClient.DataServiceResponse> onCompletion)
        {
            DataServiceContextData data = CheckParametersAndGetDataServiceContextData(verifier, context);

            verifier.VerifySaveChanges(continuation, data, (DSClient.DataServiceContext)context.Product, options, onCompletion);
        }
 /// <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);
 }