/// <summary>
 /// Clones the current link descriptor data
 /// </summary>
 /// <param name="clonedSource">The already-cloned source for the link</param>
 /// <param name="clonedTarget">The already-cloded target for the link</param>
 /// <returns>A clone of this link descriptor data</returns>
 public LinkDescriptorData Clone(EntityDescriptorData clonedSource, EntityDescriptorData clonedTarget)
 {
     var clone = new LinkDescriptorData(clonedSource, this.SourcePropertyName, clonedTarget);
     clone.ChangeOrder = this.ChangeOrder;
     clone.State = this.State;
     return clone;
 }
        /// <summary>
        /// Clones the current stream descriptor data
        /// </summary>
        /// <param name="clonedEntityDescriptor">The entity descriptor that will contain the result of this clone</param>
        /// <returns>A clone of the current stream descriptor data</returns>
        public StreamDescriptorData Clone(EntityDescriptorData clonedEntityDescriptor)
        {
            var clone = new StreamDescriptorData(clonedEntityDescriptor, this.Name);

            clone.State       = this.State;
            clone.ChangeOrder = this.ChangeOrder;

            clone.ContentType = this.ContentType;
            clone.ETag        = this.ETag;

            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.SaveStream != null)
            {
                clone.SaveStream = this.SaveStream.Clone();
            }

            return(clone);
        }
Beispiel #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LinkDescriptorData"/> class.
 /// </summary>
 /// <param name="sourceDescriptor">The entity descriptor for the source.</param>
 /// <param name="sourcePropertyName">Name of the source property.</param>
 /// <param name="targetDescriptor">The entity descriptor for the target.</param>
 internal LinkDescriptorData(EntityDescriptorData sourceDescriptor, string sourcePropertyName, EntityDescriptorData targetDescriptor)
     : base()
 {
     this.SourceDescriptor   = sourceDescriptor;
     this.TargetDescriptor   = targetDescriptor;
     this.SourcePropertyName = sourcePropertyName;
 }
        /// <summary>
        /// Calculates the DataServiceVersion for a particular EntityDescriptor
        /// </summary>
        /// <param name="entityDescriptorData">Entity Descriptor Data</param>
        /// <param name="maxProtocolVersion">The client's max protocol version</param>
        /// <returns>A Data service protocol version</returns>
        public DataServiceProtocolVersion CalculateDataServiceVersion(EntityDescriptorData entityDescriptorData, DataServiceProtocolVersion maxProtocolVersion)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            Type entityType = entityDescriptorData.Entity.GetType();
            EntityType testEntityType = this.entityModelSchema.EntityTypes.Single(et => et.FullName == entityType.FullName);

            // Calculate expected version based on type's feed mappings.
            DataServiceProtocolVersion expectedVersion = VersionHelper.CalculateEntityPropertyMappingProtocolVersion(testEntityType, VersionCalculationType.Response, MimeTypes.ApplicationAtomXml, maxProtocolVersion, maxProtocolVersion);

            // Commenting out this code pending a quick versioning discussion with pratikp and ahmed
            // Cannot check in as with code it breaks BVT's, want to get this in and update pending discussion 12-7-10
            // if (testEntityType.AllProperties.Any(p => p.IsStream()))
            // {
            //    expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4);
            // }
            // If there are bag properties then its at least v3
            if (testEntityType.HasMultiValue(true) || 
                testEntityType.HasSpatialProperties())
            {
                expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4);
            }

            return expectedVersion;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="LinkDescriptorData"/> class.
 /// </summary>
 /// <param name="sourceDescriptor">The entity descriptor for the source.</param>
 /// <param name="sourcePropertyName">Name of the source property.</param>
 /// <param name="targetDescriptor">The entity descriptor for the target.</param>
 internal LinkDescriptorData(EntityDescriptorData sourceDescriptor, string sourcePropertyName, EntityDescriptorData targetDescriptor)
     : base()
 {
     this.SourceDescriptor = sourceDescriptor;
     this.TargetDescriptor = targetDescriptor;
     this.SourcePropertyName = sourcePropertyName;
 }
Beispiel #6
0
        /// <summary>
        /// Clones the current link descriptor data
        /// </summary>
        /// <param name="clonedSource">The already-cloned source for the link</param>
        /// <param name="clonedTarget">The already-cloded target for the link</param>
        /// <returns>A clone of this link descriptor data</returns>
        public LinkDescriptorData Clone(EntityDescriptorData clonedSource, EntityDescriptorData clonedTarget)
        {
            var clone = new LinkDescriptorData(clonedSource, this.SourcePropertyName, clonedTarget);

            clone.ChangeOrder = this.ChangeOrder;
            clone.State       = this.State;
            return(clone);
        }
        /// <summary>
        /// Sets the self link.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="selfLink">The self link.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which self link has been set.</returns>
        public static EntityDescriptorData SetSelfLink(this EntityDescriptorData entityDescriptorData, Uri selfLink)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.SelfLink = selfLink;

            return(entityDescriptorData);
        }
        /// <summary>
        /// Sets the server type name.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="serverTypeName">The server type name.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which the server type name has been set.</returns>
        public static EntityDescriptorData SetServerTypeName(this EntityDescriptorData entityDescriptorData, string serverTypeName)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.ServerTypeName = serverTypeName;

            return(entityDescriptorData);
        }
        /// <summary>
        /// Sets the entity set name.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="entitySetName">Name of the entity set.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which entity set name has been set.</returns>
        public static EntityDescriptorData SetEntitySetName(this EntityDescriptorData entityDescriptorData, string entitySetName)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.EntitySetName = entitySetName;

            return(entityDescriptorData);
        }
        /// <summary>
        /// Tries to get entity descriptor data for the specified entity instance.
        /// </summary>
        /// <param name="entity">The entity instance for which to return the entity descriptor data.</param>
        /// <param name="entityData">The entity descriptor data to be retrieved.</param>
        /// <returns>True if entity descriptor data is found, false otherwise.</returns>
        /// <exception cref="TaupoArgumentNullException">
        /// When entity is null.
        /// </exception>        
        public bool TryGetEntityDescriptorData(object entity, out EntityDescriptorData entityData)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");

            entityData = this.FindEntityData(entity);

            return entityData != null;
        }
        /// <summary>
        /// Sets the identity.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="identity">The identity.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which identity has been set.</returns>
        public static EntityDescriptorData SetIdentity(this EntityDescriptorData entityDescriptorData, Uri identity)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.Identity = identity;

            return(entityDescriptorData);
        }
        /// <summary>
        /// Sets the ETag.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="value">The etag for the entity.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which ETag has been set.</returns>
        public static EntityDescriptorData SetETag(this EntityDescriptorData entityDescriptorData, string value)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.ETag = value;

            return(entityDescriptorData);
        }
        /// <summary>
        /// Sets the parent property for insert.
        /// </summary>
        /// <param name="entityDescriptorData">The entity descriptor data.</param>
        /// <param name="parentPropertyForInsert">The parent property for insert.</param>
        /// <returns><see cref="EntityDescriptorData"/> on which parent property for insert has been set.</returns>
        public static EntityDescriptorData SetParentPropertyForInsert(this EntityDescriptorData entityDescriptorData, string parentPropertyForInsert)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");

            entityDescriptorData.ParentPropertyForInsert = parentPropertyForInsert;

            return(entityDescriptorData);
        }
        private static bool IsMatch(EntityDescriptorData entityData, object entityIdentity)
        {
            //// entityIdentity could be
            ////      1. EntityDescriptorData => compare by reference
            ////      2. Entity instatnce => compare entity by reference
            ////      3. identity string => compare as strings

            ExceptionUtilities.Assert(entityData != null, "entityDescriptorData cannot be null!");
            return entityData == entityIdentity || entityData.Entity == entityIdentity || (entityData.Identity == (entityIdentity as Uri) && entityData.Identity != null);
        }
        /// <summary>
        /// Creates an entity descriptor data and puts it into the list of entity descriptors data.
        /// <see cref="ChangeStateAndChangeOrder"/> for the restrictions on the state and change order.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="changeOrder">The change order.</param>
        /// <param name="entity">The entity.</param>
        /// <returns>The created entity descriptor data.</returns>
        /// <exception cref="TaupoArgumentException">
        /// When entity is null.
        /// </exception>
        public EntityDescriptorData CreateEntityDescriptorData(EntityStates state, long changeOrder, object entity)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");

            var data = new EntityDescriptorData(this, entity);
            this.ChangeStateAndChangeOrder(data, state, changeOrder);

            this.entityDatas.Add(data);

            return data;
        }
        private bool RemoveEntityData(EntityDescriptorData entityData)
        {
            if (!this.entityDatas.Remove(entityData))
            {
                return false;
            }

            entityData.RemoveStreamDescriptorDatas();
            entityData.RemoveLinkInfoDatas();

            SetDetachedState(entityData);
            return true;
        }
        /// <summary>
        /// Creates an entity descriptor data and puts it into the list of entity descriptors data.
        /// <see cref="ChangeStateAndChangeOrder"/> for the restrictions on the state and change order.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="changeOrder">The change order.</param>
        /// <param name="identity">The identity.</param>
        /// <param name="entityClrType">The client clr type of the entity.</param>
        /// <returns>The created entity descriptor data.</returns>
        /// <exception cref="TaupoArgumentNullException">
        /// When entity is null or identity is null.
        /// </exception>
        /// <exception cref="TaupoArgumentException">
        /// When identity is empty.
        /// </exception>
        /// <exception cref="TaupoArgumentException">
        /// When state is Added. Use overload that takes entity instance as an input
        /// as in this case entity instance should always be known.
        /// </exception>
        /// <remarks>
        /// This method is useful when expressing expectations for the LoadProperty method
        /// as in this case there may be no knowledge about entity instance.
        /// When entity instance is known use overload that takes entity instance as an input.
        /// </remarks>
        public EntityDescriptorData CreateEntityDescriptorData(EntityStates state, long changeOrder, Uri identity, Type entityClrType)
        {
            if (state == EntityStates.Added)
            {
                throw new TaupoArgumentException("State cannot be Added. Use overload that takes entity as an input argument.");
            }

            var data = new EntityDescriptorData(this, identity, entityClrType)
                .SetIdentity(identity);
            this.ChangeStateAndChangeOrder(data, state, changeOrder);

            this.entityDatas.Add(data);

            return data;
        }
 /// <summary>
 /// Tracks a pending update to the given descriptor based on values read from a response payload
 /// </summary>
 /// <param name="data">The descriptor data to update</param>
 /// <param name="payload">The payload that was read</param>
 /// <param name="baseUri">The base uri of the context</param>
 public void TrackUpdateFromPayload(EntityDescriptorData data, EntityInstance payload, Uri baseUri)
 {
     ExceptionUtilities.CheckArgumentNotNull(data, "data");
     var update = new PayloadUpdate() { Descriptor = data, Payload = payload, BaseUri = baseUri };
     if (!this.IgnoreAllUpdates)
     {
         if (this.ApplyUpdatesImmediately)
         {
             Apply(update);
         }
         else
         {
             this.payloadUpdates.Add(update);
         }
     }
 }
        /// <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;
        }
        /// <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);
        }
        /// <summary>
        /// Updates the entity decriptor data with values from response headers
        /// </summary>
        /// <param name="entityDescriptorData">The descriptor data to update</param>
        /// <param name="headers">The response headers</param>
        public static void UpdateFromHeaders(this EntityDescriptorData entityDescriptorData, IDictionary <string, string> headers)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");
            ExceptionUtilities.CheckArgumentNotNull(headers, "headers");

            string etag;

            headers.TryGetValue(HttpHeaders.ETag, out etag);
            entityDescriptorData.SetETag(etag); // we do want to set null if the header is not present

            string location;

            if (headers.TryGetValue(HttpHeaders.Location, out location))
            {
                entityDescriptorData.SetEditLink(new Uri(location));
            }

            string dataServiceId;

            if (headers.TryGetValue(HttpHeaders.DataServiceId, out dataServiceId))
            {
                entityDescriptorData.SetIdentity(new Uri(dataServiceId, UriKind.RelativeOrAbsolute));
            }
        }
        /// <summary>
        /// Creates the link descriptor data and puts in into the list of link descriptors data.
        /// <see cref="ChangeStateAndChangeOrder"/> for the restrictions on the state and change order.        
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="changeOrder">The change order.</param>
        /// <param name="source">The source.</param>
        /// <param name="sourcePropertyName">Name of the source property.</param>
        /// <param name="target">The target.</param>
        /// <returns>The created link descriptor data.</returns>
        /// <exception cref="TaupoArgumentNullException">
        /// When source or sourcePropertyName is null.
        /// </exception>
        /// <exception cref="TaupoArgumentException">
        /// When sourcePropertyName is empty.
        /// </exception>
        /// <exception cref="TaupoInvalidOperationException">
        /// When entity descriptor data is not found for the source or target (if target is not null).
        /// </exception>
        public LinkDescriptorData CreateLinkDescriptorData(EntityStates state, long changeOrder, object source, string sourcePropertyName, object target)
        {
            CheckLinkArguments(source, sourcePropertyName);

            EntityDescriptorData sourceData = this.FindEntityData(source);
            if (sourceData == null)
            {
                throw new TaupoInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find entity descriptor data for the source: {0}.", source));
            }

            // Target can be null
            EntityDescriptorData targetData = null;
            if (target != null)
            {
                targetData = this.FindEntityData(target);

                if (targetData == null)
                {
                    throw new TaupoInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find entity descriptor data for the target: {0}.", target));
                }
            }

            return this.CreateLinkDescriptorData(state, changeOrder, sourceData, sourcePropertyName, targetData);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamDescriptorData"/> class.
 /// </summary>
 /// <param name="entityDescriptorData">The entity descriptor data containing the stream</param>
 internal StreamDescriptorData(EntityDescriptorData entityDescriptorData)
 {
     ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");
     this.EntityDescriptor = entityDescriptorData;
 }
            /// <summary>
            /// Normalizes the given entity payload element
            /// </summary>
            /// <param name="entityPayload">The entity payload element</param>
            /// <param name="entityDescriptorData">The descriptor data for the entity</param>
            public void Normalize(EntityInstance entityPayload, EntityDescriptorData entityDescriptorData)
            {
                ExceptionUtilities.CheckArgumentNotNull(entityPayload, "entityPayload");
                ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");
                this.typeStack.Push(entityDescriptorData.EntityClrType);

                // do this before recursing because it could be over-written by the visit method
                entityPayload.FullTypeName = entityDescriptorData.ServerTypeName;
                
                entityPayload.Id = string.Empty;
                if (entityDescriptorData.Identity != null)
                {
                    entityPayload.Id = entityDescriptorData.Identity.OriginalString;
                }

                entityPayload.Accept(this);
            }
        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;
        }
        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 CreateEntityDeleteRequest(EntityDescriptorData entityDescriptorData, SaveChangesOptions options)
        {
            var request = new ExpectedClientRequest()
            {
                Verb = HttpVerb.Delete,
                Uri = entityDescriptorData.EditLink,
            };

            request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag;
            request.Headers[HttpHeaders.Prefer] = null;
            
            request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4);

            this.SetDefaultAcceptHeader(request, options);

            request.Headers[HttpHeaders.ContentType] = null;

            string hintString = @"Entity delete\r\n{{\r\n  Descriptor = {0}\r\n}}";
            request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData);

            return request;
        }
            /// <summary>
            /// Creates EntityChangeData which captures the state of the specified entity descriptor data.
            /// </summary>
            /// <param name="entityDescriptorData">The entity descriptor data.</param>
            /// <param name="propertiesValues">The properties values before SaveChanges.</param>
            /// <returns>Entity change data.</returns>
            public static ChangeData Create(EntityDescriptorData entityDescriptorData, IEnumerable<NamedValue> propertiesValues)
            {
                EntityChangeData changeData = new EntityChangeData(entityDescriptorData);

                foreach (NamedValue nv in propertiesValues)
                {
                    changeData.CachedPropertiesValues.Add(nv.Name, nv.Value);
                }

                changeData.ClrTypeForRequery = entityDescriptorData.EntityClrType;

                return changeData;
            }
        /// <summary>
        /// Applies all pending updates for the given entity descriptor data
        /// </summary>
        /// <param name="data">The descriptor data to update</param>
        public void ApplyPendingUpdates(EntityDescriptorData data)
        {
            foreach (var update in this.headerUpdates.Where(u => u.Descriptor == data).ToList())
            {
                ExceptionUtilities.Assert(!this.ApplyUpdatesImmediately, "Should not have any pending header updates when ApplyUpdatesImmediately is true");
                Apply(update);
                this.headerUpdates.Remove(update);
            }

            foreach (var update in this.payloadUpdates.Where(u => u.Descriptor == data).ToList())
            {
                ExceptionUtilities.Assert(!this.ApplyUpdatesImmediately, "Should not have any pending payload updates when ApplyUpdatesImmediately is true");
                Apply(update);
                this.payloadUpdates.Remove(update);
            }
        }
        /// <summary>
        /// Creates the link descriptor data and puts in into the list of link descriptors data.
        /// <see cref="ChangeStateAndChangeOrder"/> for the restrictions on the state and change order.        
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="changeOrder">The change order.</param>
        /// <param name="sourceData">The source.</param>
        /// <param name="sourcePropertyName">Name of the source property.</param>
        /// <param name="targetData">The target.</param>
        /// <returns>The created link descriptor data.</returns>
        /// <exception cref="TaupoArgumentNullException">
        /// When source or sourcePropertyName is null.
        /// </exception>
        /// <exception cref="TaupoArgumentException">
        /// When sourcePropertyName is empty.
        /// </exception>
        /// <exception cref="TaupoInvalidOperationException">
        /// When entity descriptor data is not found for the source or target (if target is not null).
        /// </exception>
        public LinkDescriptorData CreateLinkDescriptorData(EntityStates state, long changeOrder, EntityDescriptorData sourceData, string sourcePropertyName, EntityDescriptorData targetData)
        {
            ExceptionUtilities.CheckArgumentNotNull(sourceData, "sourceData");
            ExceptionUtilities.CheckArgumentNotNull(sourcePropertyName, "sourcePropertyName");

            var linkData = new LinkDescriptorData(sourceData, sourcePropertyName, targetData);
            this.ChangeStateAndChangeOrder(linkData, state, changeOrder);

            this.linkDatas.Add(linkData);

            return linkData;
        }
        /// <summary>
        /// Clones the current stream descriptor data
        /// </summary>
        /// <param name="clonedEntityDescriptor">The entity descriptor that will contain the result of this clone</param>
        /// <returns>A clone of the current stream descriptor data</returns>
        public StreamDescriptorData Clone(EntityDescriptorData clonedEntityDescriptor)
        {
            var clone = new StreamDescriptorData(clonedEntityDescriptor, this.Name);
            clone.State = this.State;
            clone.ChangeOrder = this.ChangeOrder;
            
            clone.ContentType = this.ContentType;
            clone.ETag = this.ETag;
            
            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.SaveStream != null)
            {
                clone.SaveStream = this.SaveStream.Clone();
            }

            return clone;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamDescriptorData"/> class.
 /// </summary>
 /// <param name="entityDescriptorData">The entity descriptor data containing the stream</param>
 internal StreamDescriptorData(EntityDescriptorData entityDescriptorData)
 {
     ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");
     this.EntityDescriptor = entityDescriptorData;
 }
 /// <summary>
 /// Tracks a pending update to the given descriptor based on values read from response headers
 /// </summary>
 /// <param name="data">The descriptor data to update</param>
 /// <param name="headers">The headers that were read</param>
 public void TrackUpdateFromHeaders(EntityDescriptorData data, IDictionary<string, string> headers)
 {
     ExceptionUtilities.CheckArgumentNotNull(data, "data");
     var update = new HeaderUpdate() { Descriptor = data, Headers = headers };
    
     if (!this.IgnoreAllUpdates)
     {
         if (this.ApplyUpdatesImmediately)
         {
             Apply(update);
         }
         else
         {
             this.headerUpdates.Add(update);
         }
     }
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamDescriptorData"/> class.
 /// </summary>
 /// <param name="entityDescriptorData">The entity descriptor data containing the stream</param>
 /// <param name="name">The name of the stream</param>
 internal StreamDescriptorData(EntityDescriptorData entityDescriptorData, string name)
     : this(entityDescriptorData)
 {
     this.Name = name;
 }
        /// <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;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamDescriptorData"/> class.
 /// </summary>
 /// <param name="entityDescriptorData">The entity descriptor data containing the stream</param>
 /// <param name="name">The name of the stream</param>
 internal StreamDescriptorData(EntityDescriptorData entityDescriptorData, string name)
     : this(entityDescriptorData)
 {
     this.Name = name;
 }
        /// <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;
        }
        /// <summary>
        /// Updates the stream decriptor data with values from a response payload
        /// </summary>
        /// <param name="entityDescriptorData">The descriptor data to update</param>
        /// <param name="entityData">A representation of the payload</param>
        /// <param name="contextBaseUri">The base uri of the context</param>
        public static void UpdateFromPayload(this EntityDescriptorData entityDescriptorData, EntityInstance entityData, Uri contextBaseUri)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityDescriptorData, "entityDescriptorData");
            ExceptionUtilities.CheckArgumentNotNull(entityData, "entityData");

            var xmlBase = entityData.Annotations.OfType <XmlBaseAnnotation>().SingleOrDefault();

            entityDescriptorData.SetIdentity(GetAbsoluteUriForLink(contextBaseUri, xmlBase, entityData.Id));
            entityDescriptorData.SetETag(entityData.ETag);
            entityDescriptorData.SetServerTypeName(entityData.FullTypeName);

            var editLink = entityData.EditLink;

            if (editLink != null)
            {
                entityDescriptorData.SetEditLink(GetAbsoluteUriForLink(contextBaseUri, xmlBase, editLink));
            }

            var selfLink = entityData.Annotations.OfType <SelfLinkAnnotation>().SingleOrDefault();

            if (selfLink != null)
            {
                entityDescriptorData.SetSelfLink(GetAbsoluteUriForLink(contextBaseUri, xmlBase, selfLink.Value));
            }

            foreach (var navigation in entityData.Properties.OfType <NavigationPropertyInstance>())
            {
                var linkInfoData = entityDescriptorData.LinkInfos.SingleOrDefault(l => l.Name == navigation.Name);
                if (linkInfoData == null)
                {
                    linkInfoData = entityDescriptorData.CreateLinkInfoData(navigation.Name);
                }

                if (navigation.AssociationLink != null)
                {
                    linkInfoData.RelationshipLink = GetAbsoluteUriForLink(contextBaseUri, xmlBase, navigation.AssociationLink.UriString);
                }

                string navigationLink = null;
                if (navigation.Value != null)
                {
                    if (navigation.IsExpanded)
                    {
                        navigationLink = ((ExpandedLink)navigation.Value).UriString;
                    }
                    else
                    {
                        navigationLink = ((DeferredLink)navigation.Value).UriString;
                    }
                }

                if (navigationLink != null)
                {
                    linkInfoData.NavigationLink = GetAbsoluteUriForLink(contextBaseUri, xmlBase, navigationLink);
                }
            }

            entityDescriptorData.RemoveOperationDescriptorData();
            foreach (var operation in entityData.ServiceOperationDescriptors)
            {
                entityDescriptorData.CreateOperationDescriptorData(
                    new Uri(operation.Metadata, UriKind.RelativeOrAbsolute),
                    new Uri(operation.Target, UriKind.RelativeOrAbsolute),
                    operation.Title,
                    operation.IsAction);
            }

            if (entityData.IsMediaLinkEntry())
            {
                if (entityData.StreamSourceLink != null)
                {
                    entityDescriptorData.ReadStreamUri = GetAbsoluteUriForLink(contextBaseUri, xmlBase, entityData.StreamSourceLink);
                }

                if (entityData.StreamEditLink != null)
                {
                    entityDescriptorData.EditStreamUri = GetAbsoluteUriForLink(contextBaseUri, xmlBase, entityData.StreamEditLink);
                }

                entityDescriptorData.StreamETag = entityData.StreamETag;
            }

            foreach (var namedStream in entityData.Properties.OfType <NamedStreamInstance>())
            {
                var streamDescriptor = entityDescriptorData.StreamDescriptors.SingleOrDefault(n => n.Name == namedStream.Name);
                if (streamDescriptor == null)
                {
                    streamDescriptor = entityDescriptorData.CreateStreamDescriptorData(EntityStates.Unchanged, 0, namedStream.Name);
                }

                // apply self link's content type first, as the value from the edit link should 'win'
                if (namedStream.SourceLink != null)
                {
                    streamDescriptor.SelfLink    = GetAbsoluteUriForLink(contextBaseUri, xmlBase, namedStream.SourceLink);
                    streamDescriptor.ContentType = namedStream.SourceLinkContentType;
                }

                if (namedStream.EditLink != null)
                {
                    streamDescriptor.EditLink    = GetAbsoluteUriForLink(contextBaseUri, xmlBase, namedStream.EditLink);
                    streamDescriptor.ContentType = namedStream.EditLinkContentType;
                }

                streamDescriptor.ETag = namedStream.ETag;
            }

            // TODO: update property values from payload
        }
        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>
        /// 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);
        }
        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));
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="EntityChangeData"/> class.
 /// </summary>
 /// <param name="entityDescriptorData">The entity descriptor data.</param>
 protected EntityChangeData(EntityDescriptorData entityDescriptorData)
     : base(entityDescriptorData)
 {
     this.CachedPropertiesValues = new Dictionary<string, object>();
 }