/// <summary>
        /// Tracks a call to SetSaveStream
        /// </summary>
        /// <param name="data">The context data.</param>
        /// <param name="entity">The entity.</param>
        /// <param name="name">The name of the stream or null to indicate the default stream.</param>
        /// <param name="saveStreamLogger">A logger for the save stream</param>
        /// <param name="closeStream">A valud indicating whether or not to close the stream</param>
        /// <param name="headers">The headers for the request</param>
        public static void TrackSetSaveStream(this DataServiceContextData data, object entity, string name, IStreamLogger saveStreamLogger, bool closeStream, IEnumerable<KeyValuePair<string, string>> headers)
        {
            ExceptionUtilities.CheckArgumentNotNull(data, "data");
            ExceptionUtilities.CheckArgumentNotNull(saveStreamLogger, "saveStreamLogger");
            ExceptionUtilities.CheckArgumentNotNull(headers, "headers");

            EntityDescriptorData descriptorData;
            ExceptionUtilities.Assert(data.TryGetEntityDescriptorData(entity, out descriptorData), "Cannot set save stream on an entity that is not being tracked");

            StreamDescriptorData streamDescriptor;
            if (name == null)
            {
                if (descriptorData.State == EntityStates.Added)
                {
                    descriptorData.DefaultStreamState = EntityStates.Added;
                }
                else
                {
                    descriptorData.DefaultStreamState = EntityStates.Modified;
                }

                streamDescriptor = descriptorData.DefaultStreamDescriptor;
            }
            else
            {
                streamDescriptor = descriptorData.StreamDescriptors.SingleOrDefault(n => n.Name == name);
                if (streamDescriptor == null)
                {
                    streamDescriptor = descriptorData.CreateStreamDescriptorData(EntityStates.Modified, data.GetNextChangeOrder(), name);
                }
                else
                {
                    data.ChangeStateAndChangeOrder(streamDescriptor, EntityStates.Modified, data.GetNextChangeOrder());
                }
            }

            // we need to trap what the product reads from the stream to compare it against what is sent on the wire
            streamDescriptor.SaveStream = new SaveStreamData(saveStreamLogger, closeStream, headers);
        }
        /// <summary>
        /// Tracks the DeleteLink method.
        /// </summary>
        /// <param name="data">The data service context data on which to apply state transition.</param>
        /// <param name="source">The source.</param>
        /// <param name="sourcePropertyName">Name of the source property.</param>
        /// <param name="target">The target.</param>
        public static void TrackDeleteLink(this DataServiceContextData data, object source, string sourcePropertyName, object target)
        {
            EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot delete link:", "source");

            EntityDescriptorData targetDescriptorData = GetTrackedEntityDescriptorData(data, target, "Cannot delete link:", "target");

            LinkDescriptorData descriptorData;
            if (data.TryGetLinkDescriptorData(sourceDescriptorData, sourcePropertyName, targetDescriptorData, out descriptorData)
                && descriptorData.State == EntityStates.Added)
            {
                data.RemoveDescriptorData(descriptorData);
            }
            else
            {
                CheckStateIsNot(EntityStates.Added, sourceDescriptorData, "Cannot delete link:", "source");
                CheckStateIsNot(EntityStates.Added, targetDescriptorData, "Cannot delete link:", "target");

                if (descriptorData == null)
                {
                    data.CreateLinkDescriptorData(EntityStates.Deleted, data.GetNextChangeOrder(), sourceDescriptorData, sourcePropertyName, targetDescriptorData);
                }
                else if (descriptorData.State != EntityStates.Deleted)
                {
                    data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Deleted, data.GetNextChangeOrder());
                }
            }
        }
 /// <summary>
 /// Tracks the UpdateObject method.
 /// </summary>
 /// <param name="data">The data service context data on which to apply state transition.</param>
 /// <param name="entity">The entity.</param>
 public static void TrackUpdateObject(this DataServiceContextData data, object entity)
 {
     var descriptorData = GetTrackedEntityDescriptorData(data, entity, "Failed to track UpdateObject:", "entity");
     if (descriptorData.State == EntityStates.Unchanged)
     {
         data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Modified, data.GetNextChangeOrder());
     }
 }
        /// <summary>
        /// Tracks the SetLink method.
        /// </summary>
        /// <param name="data">The data service context data on which to apply state transition.</param>
        /// <param name="source">The source.</param>
        /// <param name="sourcePropertyName">Name of the source property.</param>
        /// <param name="target">The target.</param>
        public static void TrackSetLink(this DataServiceContextData data, object source, string sourcePropertyName, object target)
        {
            ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(sourcePropertyName, "sourcePropertyName");

            EntityDescriptorData sourceDescriptorData = GetTrackedEntityDescriptorData(data, source, "Cannot set link:", "source");
            CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot set link:", "source");

            EntityDescriptorData targetDescriptorData = null;
            if (target != null)
            {
                targetDescriptorData = GetTrackedEntityDescriptorData(data, target, "Cannot set link:", "target");
                CheckStateIsNot(EntityStates.Deleted, sourceDescriptorData, "Cannot set link:", "target");
            }

            var relatedToSource = data.LinkDescriptorsData.Where(e => e.SourceDescriptor.Entity == source && e.SourcePropertyName == sourcePropertyName).ToList();

            if (relatedToSource.Count > 1)
            {
                throw new TaupoInvalidOperationException("Cannot set link: source contains multiple links for the property: " + sourcePropertyName);
            }

            LinkDescriptorData existingLinkDescriptorData = relatedToSource.FirstOrDefault();

            if (existingLinkDescriptorData == null)
            {
                data.CreateLinkDescriptorData(EntityStates.Modified, data.GetNextChangeOrder(), sourceDescriptorData, sourcePropertyName, targetDescriptorData);
            }
            else
            {
                if (existingLinkDescriptorData.State != EntityStates.Modified || existingLinkDescriptorData.TargetDescriptor != targetDescriptorData)
                {
                    data.ChangeStateAndChangeOrder(existingLinkDescriptorData, EntityStates.Modified, data.GetNextChangeOrder());
                }

                existingLinkDescriptorData.TargetDescriptor = targetDescriptorData;
            }
        }
 /// <summary>
 /// Tracks the DeleteObject method.
 /// </summary>
 /// <param name="data">The data service context data on which to apply state transition.</param>
 /// <param name="entity">The entity.</param>
 public static void TrackDeleteObject(this DataServiceContextData data, object entity)
 {
     var descriptorData = GetTrackedEntityDescriptorData(data, entity, "Failed to track DeleteObject:", "entity");
     if (descriptorData.State == EntityStates.Added)
     {
         data.RemoveDescriptorData(descriptorData);
     }
     else if (descriptorData.State != EntityStates.Deleted)
     {
         data.ChangeStateAndChangeOrder(descriptorData, EntityStates.Deleted, data.GetNextChangeOrder());
     }
 }