Beispiel #1
0
        private void UpdateBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues)
        {
            List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList();

            if (!exactMatches.Any())
            {
                return;
            }

            ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath);

            NamedValue expectedBagValue = exactMatches.Single();
            var        bagType          = memberProperty.PropertyType as QueryCollectionType;

            if (expectedBagValue.Value == null)
            {
                instance.SetValue(memberProperty.Name, bagType.NullValue);
                this.unusedNamedValuePaths.Remove(expectedBagValue.Name);
            }
            else if (expectedBagValue.Value == EmptyData.Value)
            {
                QueryCollectionValue value = bagType.CreateCollectionWithValues(new QueryValue[] { });
                instance.SetValue(memberProperty.Name, value);
                this.unusedNamedValuePaths.Remove(expectedBagValue.Name);
            }
        }
Beispiel #2
0
        private void CompareBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues)
        {
            List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList();

            if (!exactMatches.Any())
            {
                return;
            }

            ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath);

            QueryCollectionValue actualQueryBagValue = instance.GetCollectionValue(memberProperty.Name);
            NamedValue           expectedBagValue    = exactMatches.Single();

            if (expectedBagValue.Value == null)
            {
                this.WriteErrorIfNotNull(propertyPath, actualQueryBagValue);
                this.unusedNamedValuePaths.Remove(propertyPath);
            }
            else if (expectedBagValue.Value == EmptyData.Value)
            {
                if (!this.WriteErrorIfNull(propertyPath, actualQueryBagValue))
                {
                    this.WriteErrorIfNotEqual(propertyPath, actualQueryBagValue.Elements.Count, 0, "Expected zero elements in BagProperty");
                    this.unusedNamedValuePaths.Remove(propertyPath);
                }
            }
        }
        /// <summary>
        /// Extends value population to include stream data
        /// </summary>
        /// <param name="row">The row containing the data</param>
        /// <param name="instance">The structural instacne</param>
        protected override void PopulateInstanceFromRow(EntitySetDataRow row, QueryStructuralValue instance)
        {
            base.PopulateInstanceFromRow(row, instance);
            var rowWithStreams = row as EntitySetDataRowWithStreams;
            if (rowWithStreams != null)
            {
                foreach (var stream in rowWithStreams.Streams)
                {
                    if (stream.IsEditLinkBasedOnConvention)
                    {
                        ExceptionUtilities.CheckObjectNotNull(this.LinkGenerator, "Cannot compute convention-based edit link without injected generator");
                        stream.EditLink = this.LinkGenerator.GenerateStreamEditLink(instance, stream.Name);

                        // for the default stream, there must always be a self-link
                        if (stream.Name == null && stream.SelfLink == null)
                        {
                            stream.SelfLink = stream.EditLink;
                        }
                    }

                    instance.SetStreamValue(stream.Name, stream.ContentType, stream.ETag, stream.EditLink, stream.SelfLink, stream.Content);
                }
            }

            instance.MarkDynamicPropertyValues();
        }
        /// <summary>
        /// Compares a queryStructualValue to namedValues
        /// Throws a DataComparisonException if values don't match
        /// </summary>
        /// <param name="queryStructuralValue">QueryStructural Value to compare</param>
        /// <param name="namedValues">NamedValues to compare</param>
        /// <param name="scalarComparer">The comparer to use for scalar values</param>
        public void Compare(QueryStructuralValue queryStructuralValue, IEnumerable<NamedValue> namedValues, IQueryScalarValueToClrValueComparer scalarComparer)
        {
            ExceptionUtilities.CheckArgumentNotNull(queryStructuralValue, "queryStructuralValue");
            ExceptionUtilities.CheckArgumentNotNull(namedValues, "namedValues");
            ExceptionUtilities.CheckArgumentNotNull(scalarComparer, "scalarComparer");

            this.errors = new List<string>();
            this.unusedNamedValuePaths = new List<string>(namedValues.Select(s => s.Name).ToArray());
            this.comparer = scalarComparer;

            this.CompareValues(queryStructuralValue, namedValues, null);
            
            foreach (string propertyPath in this.unusedNamedValuePaths)
            {
                this.errors.Add(string.Format(CultureInfo.InvariantCulture, "Value for propertyPath {0} was not compared against queryStructuralValue", propertyPath));
            }

            string errorMessage = null;
            foreach (string error in this.errors)
            {
                if (errorMessage == null)
                {
                    errorMessage = error;
                }
                else
                {
                    errorMessage = errorMessage + ", \r\n" + error;
                }
            }

            if (errorMessage != null)
            {
                throw new DataComparisonException(errorMessage);
            }
        }
Beispiel #5
0
        /// <summary>
        /// Extends value population to include stream data
        /// </summary>
        /// <param name="row">The row containing the data</param>
        /// <param name="instance">The structural instacne</param>
        protected override void PopulateInstanceFromRow(EntitySetDataRow row, QueryStructuralValue instance)
        {
            base.PopulateInstanceFromRow(row, instance);
            var rowWithStreams = row as EntitySetDataRowWithStreams;

            if (rowWithStreams != null)
            {
                foreach (var stream in rowWithStreams.Streams)
                {
                    if (stream.IsEditLinkBasedOnConvention)
                    {
                        ExceptionUtilities.CheckObjectNotNull(this.LinkGenerator, "Cannot compute convention-based edit link without injected generator");
                        stream.EditLink = this.LinkGenerator.GenerateStreamEditLink(instance, stream.Name);

                        // for the default stream, there must always be a self-link
                        if (stream.Name == null && stream.SelfLink == null)
                        {
                            stream.SelfLink = stream.EditLink;
                        }
                    }

                    instance.SetStreamValue(stream.Name, stream.ContentType, stream.ETag, stream.EditLink, stream.SelfLink, stream.Content);
                }
            }

            instance.MarkDynamicPropertyValues();
        }
        private void PopulateNavigations(EntitySetDataRow row, QueryStructuralValue instance)
        {
            var                 entityInstance = instance as QueryEntityValue;
            EntitySetData       entitySetData  = row.Parent;
            EntityContainerData containerData  = entitySetData.Parent;

            foreach (AssociationSet associationSet in containerData.EntityContainer.AssociationSets)
            {
                foreach (var fromSetEnd in associationSet.Ends.Where(e => e.EntitySet == entitySetData.EntitySet))
                {
                    var fromEntityType = fromSetEnd.AssociationEnd.EntityType;
                    if (!row.EntityType.IsKindOf(fromEntityType))
                    {
                        continue;
                    }

                    var toSetEnd = associationSet.GetOtherEnd(fromSetEnd);

                    this.PopulateNavigateResult(row, entityInstance, associationSet, fromSetEnd, toSetEnd);

                    // if Navigation property exists, populate it as well
                    var navProp = row.EntityType.AllNavigationProperties.SingleOrDefault(p => p.Association == associationSet.AssociationType &&
                                                                                         p.ToAssociationEnd == toSetEnd.AssociationEnd);
                    if (navProp != null)
                    {
                        instance.SetValue(navProp.Name, entityInstance.GetNavigateResult(navProp.Association, navProp.ToAssociationEnd));
                    }
                }
            }
        }
Beispiel #7
0
        private void UpdateVisibleEntitiesGraphForEntity(QueryStructuralValue entityValue, string path, string[] spanPaths)
        {
            if (!this.VisibleEntitiesGraph.ContainsKey(entityValue))
            {
                if (entityValue.IsNull)
                {
                    this.VisibleEntitiesGraph[entityValue] = entityValue;

                    return;
                }
                else
                {
                    var visibleEntity = entityValue.Type.CreateNewInstance();
                    this.PopulateNonNavigationPropertiesOfVisibleEntity(entityValue, visibleEntity);
                    this.VisibleEntitiesGraph[entityValue] = visibleEntity;
                }
            }

            if (!entityValue.IsNull)
            {
                foreach (QueryProperty property in entityValue.Type.Properties)
                {
                    string newPath = this.CreateNewPath(path, property.Name);
                    if (this.IsValidSpanPath(newPath, spanPaths))
                    {
                        var originalValue = entityValue.GetValue(property.Name);
                        this.UpdateVisibleEntitiesGraphForQueryValue(originalValue, newPath, spanPaths);
                    }
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// Verifies the default stream.
        /// </summary>
        /// <param name="dataContext">The data context.</param>
        /// <param name="queryEntityValue">The query entity value.</param>
        /// <param name="ed">The entity descriptor.</param>
        /// <param name="continuation">The stream descriptor continuation.</param>
        private void VerifyDefaultStream(DataServiceContext dataContext, QueryStructuralValue queryEntityValue, EntityDescriptor ed, IAsyncContinuation continuation)
        {
            var expectedStreamValue = queryEntityValue.GetDefaultStreamValue();

            this.VerifyStreamDescriptorValues(expectedStreamValue, null, null, ed.StreamETag, ed.EditStreamUri, ed.ReadStreamUri);

            var expectedReadStreamUri = GetExpectedReadStreamUri(expectedStreamValue);

            Uri readStreamUri = dataContext.GetReadStreamUri(ed.Entity);

            this.Assert.AreEqual(expectedReadStreamUri, readStreamUri, "Read stream uri did not match for default stream");

            dataContext.GetReadStream(
                continuation,
                this.isAsynchronous,
                ed.Entity,
                null,
                new DataServiceRequestArgs()
            {
            },
                response =>
            {
                UpdateStreamValueFromHeaders(expectedStreamValue, response);

                this.VerifyStreamDescriptorValues(expectedStreamValue, null, null, ed.StreamETag, ed.EditStreamUri, ed.ReadStreamUri);

                // skip this verification when using payload driven verification since we don't have the expected content for streams
                if (!this.DataProviderSettings.UsePayloadDrivenVerification)
                {
                    this.Assert.IsTrue(this.VerifyStreams(expectedStreamValue, response), "Failed to compare the default stream");
                }

                continuation.Continue();
            });
        }
Beispiel #9
0
        /// <summary>
        /// Verifies the named streams.
        /// </summary>
        /// <param name="dataContext">The data context.</param>
        /// <param name="queryEntityValue">The query entity value.</param>
        /// <param name="streamProperty">The stream property.</param>
        /// <param name="ed">The entity descriptor</param>
        /// <param name="continuation">The stream descriptor continuation.</param>
        private void VerifyNamedStreams(DataServiceContext dataContext, QueryStructuralValue queryEntityValue, QueryProperty streamProperty, EntityDescriptor ed, IAsyncContinuation continuation)
        {
            var expectedStreamValue = queryEntityValue.GetStreamValue(streamProperty.Name);
            var streamDescriptor    = ed.StreamDescriptors.SingleOrDefault(s => s.StreamLink.Name == streamProperty.Name);

            this.Assert.IsNotNull(streamDescriptor, "Entity missing stream descriptor for stream '{0}'", streamProperty.Name);
            this.VerifyStreamLink(expectedStreamValue, streamDescriptor.StreamLink);

            var expectedReadStreamUri = GetExpectedReadStreamUri(expectedStreamValue);

            Uri readStreamUri = dataContext.GetReadStreamUri(ed.Entity, streamProperty.Name);

            this.Assert.AreEqual(expectedReadStreamUri, readStreamUri, "Read stream uri did not match for stream '{0}'", streamProperty.Name);

            dataContext.GetReadStream(
                continuation,
                this.isAsynchronous,
                ed.Entity,
                streamProperty.Name,
                new DataServiceRequestArgs()
            {
            },
                response =>
            {
                UpdateStreamValueFromHeaders(expectedStreamValue, response);
                this.VerifyStreamLink(expectedStreamValue, streamDescriptor.StreamLink);
                // skip this verification when using payload driven verification since we don't have the expected content for streams
                if (!this.DataProviderSettings.UsePayloadDrivenVerification)
                {
                    this.Assert.IsTrue(this.VerifyStreams(expectedStreamValue, response), "Failed to compare value of stream '{0}'", streamProperty.Name);
                }

                continuation.Continue();
            });
        }
Beispiel #10
0
        /// <summary>
        /// Adds stream properties to structural value.
        /// </summary>
        /// <param name="entity">The target structural value.</param>
        /// <param name="payload">The payload with the entity values.</param>
        /// <param name="xmlBaseAnnotations">The xml base annotations ancestors and local element</param>
        private void AddStreamProperties(QueryStructuralValue entity, EntityInstance payload, IEnumerable <XmlBaseAnnotation> xmlBaseAnnotations)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");
            ExceptionUtilities.CheckArgumentNotNull(payload, "payload");

            var xmlBaseAnnotationsList = xmlBaseAnnotations.ToList();

            if (payload.IsMediaLinkEntry())
            {
                string editLink = this.NormalizeLinkFromPayload(payload.StreamEditLink, xmlBaseAnnotationsList);
                string selfLink = this.NormalizeLinkFromPayload(payload.StreamSourceLink, xmlBaseAnnotationsList);

                entity.SetStreamValue(null, payload.StreamContentType, payload.StreamETag, editLink, selfLink, new byte[0]);
            }

            foreach (var stream in payload.Properties.OfType <NamedStreamInstance>())
            {
                string contentType = stream.SourceLinkContentType;
                if (contentType == null)
                {
                    contentType = stream.EditLinkContentType;
                }

                string editLink = this.NormalizeLinkFromPayload(stream.EditLink, xmlBaseAnnotationsList);
                string selfLink = this.NormalizeLinkFromPayload(stream.SourceLink, xmlBaseAnnotationsList);

                entity.SetStreamValue(stream.Name, contentType, stream.ETag, editLink, selfLink, new byte[0]);
            }
        }
        /// <summary>
        /// Visits a QueryStructuralValue and returns the clr value of the structural value
        /// </summary>
        /// <param name="value">The QueryStructuralValue which contains the clr value of the structural type : complex/entity  type</param>
        /// <returns>The clr instance of the structural value</returns>
        public object Visit(QueryStructuralValue value)
        {
            object clrInstance = null;

            if (this.objectLookup.TryGetValue(value, out clrInstance))
            {
                return(clrInstance);
            }

            ExceptionUtilities.CheckObjectNotNull(value.Type as IQueryClrType, "Structural type does not implement IQueryClrType");

            IQueryClrType clrTypeQueryable = value.Type as IQueryClrType;
            Type          clrType          = clrTypeQueryable.ClrType;

            ExceptionUtilities.CheckObjectNotNull(clrType, "ClrType should not be null");

            clrInstance = clrType.GetConstructor(Type.EmptyTypes).Invoke(null);
            this.objectLookup.Add(value, clrInstance);

            foreach (var member in value.MemberNames)
            {
                QueryValue   queryValue     = value.GetValue(member);
                var          memberValue    = queryValue.Accept(this);
                PropertyInfo memberProperty = clrType.GetProperty(member);
                memberProperty.SetValue(clrInstance, memberValue, null);
            }

            return(clrInstance);
        }
        private QueryValue BuildCollectionQueryValue(string propertyPath, QueryCollectionType queryCollectionType, EntitySetDataRow row)
        {
            QueryScalarType   scalarElementDataType      = queryCollectionType.ElementType as QueryScalarType;
            QueryComplexType  complexTypeElementDataType = queryCollectionType.ElementType as QueryComplexType;
            List <QueryValue> queryValues = new List <QueryValue>();

            if (scalarElementDataType != null)
            {
                int i = 0;
                while (row.PropertyPaths.Any(pp => pp == propertyPath + i))
                {
                    var value = row[propertyPath + i];
                    queryValues.Add(scalarElementDataType.CreateValue(value));
                    i++;
                }
            }
            else
            {
                ExceptionUtilities.CheckObjectNotNull(complexTypeElementDataType, "PropertyPath '{0}' is an invalid type '{1}'", propertyPath, queryCollectionType.ElementType);

                int i = 0;
                while (row.PropertyPaths.Where(pp => pp.StartsWith(propertyPath + i, StringComparison.Ordinal)).Count() > 0)
                {
                    QueryStructuralValue complexChildInstance = complexTypeElementDataType.CreateNewInstance();
                    this.BuildStructuralPropertiesQueryValue(complexChildInstance, propertyPath + i + ".", complexTypeElementDataType.ComplexType.Properties, complexTypeElementDataType.Properties, row);
                    queryValues.Add(complexChildInstance);
                    i++;
                }
            }

            return(queryCollectionType.CreateCollectionWithValues(queryValues.ToArray()));
        }
        /// <summary>
        /// Removes the the entity with the given key from the given the given property on the given structural value
        /// </summary>
        /// <param name="keyToRemove">The key of the entity to remove</param>
        /// <param name="related">The structural value to remove from</param>
        /// <param name="navigation">The property to remove from</param>
        private void RemoveFromNavigationProperty(EntityDataKey keyToRemove, QueryStructuralValue related, NavigationProperty navigation)
        {
            if (navigation.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
            {
                var collection = related.GetCollectionValue(navigation.Name);

                // remove the element with the given key from the collection, if it is present
                foreach (var element in collection.Elements.Cast <QueryStructuralValue>().ToList())
                {
                    if (this.GetEntityKey(element).Equals(keyToRemove))
                    {
                        collection.Elements.Remove(element);
                    }
                }
            }
            else
            {
                // if the value's key matches, set it to null
                var value = related.GetStructuralValue(navigation.Name);
                if (!value.IsNull && this.GetEntityKey(value).Equals(keyToRemove))
                {
                    related.SetValue(navigation.Name, value.Type.NullValue);
                }
            }
        }
        /// <summary>
        /// Gets the expected query value for the action request
        /// </summary>
        /// <param name="initialExpectedResults">Initial expected values for an action</param>
        /// <param name="parameterValues">Parameter values for the action</param>
        /// <returns>A query Value that is the expected value</returns>
        public QueryValue GetExpectedQueryValue(QueryValue initialExpectedResults, params QueryValue[] parameterValues)
        {
            ExceptionUtilities.CheckArgumentNotNull(initialExpectedResults, "initialExpectedResults");

            QueryStructuralValue initialStructuralValue = initialExpectedResults as QueryStructuralValue;

            ExceptionUtilities.CheckArgumentNotNull(initialStructuralValue, "initialStructuralValue");

            QueryScalarValue initialScalarValue = initialStructuralValue.GetScalarValue(this.IntegerProperty);

            ExceptionUtilities.CheckArgumentNotNull(initialScalarValue, "initialScalarValue");

            int intPropertyValue = (int)initialScalarValue.Value;

            ExceptionUtilities.CheckArgumentNotNull(intPropertyValue, "intPropertyValue");

            if (intPropertyValue != int.MaxValue)
            {
                initialStructuralValue.SetPrimitiveValue(this.IntegerProperty, intPropertyValue + 1);
            }
            else
            {
                initialStructuralValue.SetPrimitiveValue(this.IntegerProperty, 0);
            }

            return(initialStructuralValue);
        }
Beispiel #15
0
        private void ComparePrimitiveBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues)
        {
            int i = 0;

            var collection = instance.GetCollectionValue(memberProperty.Name);

            if (!this.WriteErrorIfNull(propertyPath, collection))
            {
                List <NamedValue> primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList();
                while (primitiveItemNamedValues.Any())
                {
                    if (i < collection.Elements.Count)
                    {
                        var scalarQueryValue = collection.Elements[i] as QueryScalarValue;
                        ExceptionUtilities.Assert(primitiveItemNamedValues.Count() < 2, "Should not get more than one value for a primitive Bag item for path '{0}'", propertyPath + "." + i);
                        var value = primitiveItemNamedValues.Single();
                        this.WriteErrorIfNotEqual(propertyPath, value.Value, scalarQueryValue);
                        this.unusedNamedValuePaths.Remove(value.Name);
                    }

                    i++;
                    primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList();
                }

                this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath);
            }
        }
Beispiel #16
0
        /// <summary>
        /// Visits the QueryStructuralValue
        /// </summary>
        /// <param name="value">Value to visit</param>
        /// <returns>a string that represents a query structural value</returns>
        public string Visit(QueryStructuralValue value)
        {
            var indent          = this.GenerateIndent();
            var queryEntityType = value.Type as QueryEntityType;

            if (queryEntityType != null)
            {
                var idValue = this.LinkGenerator.GenerateEntityId(value).Replace(this.AstoriaServiceDescriptor.ServiceUri.AbsoluteUri, string.Empty);
                this.builder.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}Id:{1}", indent, idValue));

                // do not expand below configured level of expands likely writing out querydataset
                if (this.currentDepth > this.maxDepth)
                {
                    return(null);
                }

                foreach (var member in queryEntityType.Properties.Where(p => p.IsNavigationProperty()))
                {
                    var memberValue = value.GetValue(member.Name);
                    this.currentDepth++;
                    var childIndent = this.GenerateIndent();
                    this.builder.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}{1}", childIndent, member.Name));
                    this.currentDepth++;
                    memberValue.Accept(this);
                    this.currentDepth -= 2;
                }
            }

            return(null);
        }
Beispiel #17
0
        /// <summary>
        /// Gets the pre and post update representations of the entity updated by the given request
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="beforeUpdate">The entity before the update</param>
        /// <param name="afterUpdate">The entity after the update</param>
        public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests");

            KeyValuePair <QueryStructuralValue, QueryStructuralValue> beforeAndAfter;

            if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter))
            {
                var entityUri = request.Uri.ScopeToEntity();
                var entity    = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false);

                beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity));

                afterUpdate = entity;

                beforeAndAfter = new KeyValuePair <QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate);
                this.updatedEntityCache[request] = beforeAndAfter;
            }
            else
            {
                beforeUpdate = beforeAndAfter.Key;
                afterUpdate  = beforeAndAfter.Value;
            }
        }
Beispiel #18
0
        public static void SetStreamValue(this QueryStructuralValue instance, string namedStream, AstoriaQueryStreamValue value)
        {
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");
            ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(namedStream, "namedStream");

            instance.AssertPropertyType <AstoriaQueryStreamType>(namedStream);

            instance.SetValue(namedStream, value);
        }
 private static void InitMemberStreamTypes(QueryEntityType type, QueryStructuralValue structural)
 {
     // initialize named streams
     foreach (var namedStream in type.Properties.Streams())
     {
         AstoriaQueryStreamValue qsv = new AstoriaQueryStreamValue((AstoriaQueryStreamType)namedStream.PropertyType, (byte[])null, null, type.EvaluationStrategy);
         structural.SetStreamValue(namedStream.Name, qsv);
     }
 }
Beispiel #20
0
        private void FixupBothEndsOfNavigationProperty(QueryStructuralValue fromValue, QueryStructuralValue toValue, NavigationProperty navigationProperty)
        {
            this.FixupNavigationProperty(fromValue, toValue, navigationProperty.Name);
            var otherSideNavigation = navigationProperty.ToAssociationEnd.EntityType.AllNavigationProperties.Where(p => p.Association == navigationProperty.Association && p.FromAssociationEnd == navigationProperty.ToAssociationEnd && p.ToAssociationEnd == navigationProperty.FromAssociationEnd).SingleOrDefault();

            if (otherSideNavigation != null && !toValue.IsNull)
            {
                this.FixupNavigationProperty(toValue, fromValue, otherSideNavigation.Name);
            }
        }
Beispiel #21
0
        /// <summary>
        /// Fixes up the properties of the structural value based on the given selected paths
        /// </summary>
        /// <param name="instance">The value to fix up</param>
        /// <param name="selectedPaths">The selected paths for the value's scope</param>
        /// <returns>The fixed-up value</returns>
        private QueryStructuralValue FixupPropertiesForSelect(QueryStructuralValue instance, IEnumerable <string> selectedPaths)
        {
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");
            ExceptionUtilities.CheckCollectionNotEmpty(selectedPaths, "selectedPaths");

            var entityType = instance.Type as QueryEntityType;

            ExceptionUtilities.CheckObjectNotNull(entityType, "Select is not supported on non-entity types");

            var masked = MaskedQueryStructuralValue.Create(instance);

            bool wildCard = selectedPaths.Contains(Endpoints.SelectAll);

            var propertyNames = entityType.EntityType.AllProperties.Select(p => p.Name).Concat(entityType.Properties.Streams().Select(m => m.Name));

            foreach (string propertyName in propertyNames)
            {
                if (!selectedPaths.Contains(Uri.EscapeDataString(propertyName)) && !wildCard)
                {
                    // Primitive, bag and stream properties are either entirely present or entirely missing.
                    // However, complex properties can be partially present if a mapped sub-property has a
                    // null value. For this reason, we recursively hide the property and all sub-properties.
                    HidePropertyRecursive(masked, propertyName);
                }
            }

            foreach (string propertyName in entityType.EntityType.AllNavigationProperties.Select(p => p.Name))
            {
                string pathMarker = Uri.EscapeDataString(propertyName) + "/";
                var    subpaths   = selectedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length));

                var value = instance.GetValue(propertyName);
                ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName);

                if (selectedPaths.Contains(Uri.EscapeDataString(propertyName)))
                {
                    continue;
                }
                else if (subpaths.Any())
                {
                    var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, subpaths));
                    masked.SetValue(propertyName, maskedValue);
                }
                else if (wildCard)
                {
                    masked.SetValue(propertyName, value.Type.NullValue);
                }
                else
                {
                    masked.HideMember(propertyName);
                }
            }

            return(masked);
        }
Beispiel #22
0
        /// <summary>
        /// Fixes up the properties of the structural value based on the given expanded paths
        /// </summary>
        /// <param name="instance">The value to fix up</param>
        /// <param name="expandedPaths">The expanded paths for the value's scope</param>
        /// <returns>The fixed-up value</returns>
        private QueryStructuralValue FixupPropertiesForExpand(QueryStructuralValue instance, IEnumerable <string> expandedPaths)
        {
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");
            ExceptionUtilities.CheckArgumentNotNull(expandedPaths, "expandedPaths");

            var entityType = instance.Type as QueryEntityType;

            ExceptionUtilities.CheckObjectNotNull(entityType, "Expand is not supported on non-entity types");

            var masked = MaskedQueryStructuralValue.Create(instance);

            foreach (var navigation in entityType.EntityType.AllNavigationProperties)
            {
                string expandedPath;
                string propertyName = navigation.Name;
                var    value        = instance.GetValue(propertyName);

                ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName);

                bool   match      = TryGetExpandedPath(expandedPaths, propertyName, entityType.EntityType, out expandedPath);
                string pathMarker = expandedPath + "/";
                var    subpaths   = expandedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length));

                // note that if there is match, we still need to fix up up any children, even if there are no subpaths
                if (subpaths.Any() || match)
                {
                    // recurse
                    var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, subpaths));

                    // handle page limit on expanded collections
                    if (maskedValue.Type is QueryCollectionType)
                    {
                        var collection = maskedValue as QueryCollectionValue;
                        ExceptionUtilities.CheckObjectNotNull(collection, "Value was a collection type, but not a collection value");

                        var relatedSet         = entityType.EntitySet.GetRelatedEntitySet(navigation);
                        var relatedSetPageSize = relatedSet.GetEffectivePageSize();
                        if (relatedSetPageSize.HasValue && this.applyPagingInExpands)
                        {
                            collection  = this.AddOrderingForPagingToQueryCollectionValue(collection);
                            maskedValue = collection.Take(relatedSetPageSize.Value);
                        }
                    }

                    masked.SetValue(propertyName, maskedValue);
                }
                else if (!match)
                {
                    // set any non-expanded navigation properties to null
                    masked.SetValue(propertyName, value.Type.NullValue);
                }
            }

            return(masked);
        }
Beispiel #23
0
 private void PopulateNonNavigationPropertiesOfVisibleEntity(QueryStructuralValue originalEntity, QueryStructuralValue visibleEntity)
 {
     foreach (var property in originalEntity.Type.Properties)
     {
         bool isCollectionOfNonEntities = property.PropertyType is QueryCollectionType && !(((QueryCollectionType)property.PropertyType).ElementType is QueryEntityType);
         if (property.PropertyType is QueryScalarType || property.PropertyType is QueryComplexType || isCollectionOfNonEntities)
         {
             visibleEntity.SetValue(property.Name, originalEntity.GetValue(property.Name));
         }
     }
 }
Beispiel #24
0
        private static void MarkPropertyAsDynamic(QueryStructuralValue value, string memberName)
        {
            var propertyValue = value.GetValue(memberName);

            if (propertyValue.Type is QueryComplexType)
            {
                MarkDynamicSubPropertyValues((QueryStructuralValue)propertyValue);
            }

            propertyValue.AsDynamicPropertyValue();
        }
        /// <summary>
        /// Compares anonymous object with the expected value.
        /// </summary>
        /// <param name="expected">Expected value.</param>
        /// <param name="actual">Actual value.</param>
        /// <param name="path">The path to the compared object (for debugging purposes).</param>
        /// <param name="shouldThrow">Should exception be thrown if error is encountered.</param>
        /// <returns>Result of the comparison, Success, Failure or Skipped.</returns>
        protected ComparisonResult CompareAnonymous(QueryStructuralValue expected, object actual, string path, bool shouldThrow)
        {
            if (actual != null && !actual.GetType().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any())
            {
                this.ThrowOrLogError(shouldThrow, "Expecting anonymous type in '{0}'. Got: {1}.", path, actual.GetType());

                return(ComparisonResult.Failure);
            }

            return(this.CompareStructural(expected, actual, path, shouldThrow));
        }
Beispiel #26
0
        private void SetCollectionProperty(QueryStructuralValue instance, QueryProperty memberProperty, List <QueryValue> collectionElements)
        {
            QueryCollectionValue queryCollectionValue = instance.GetCollectionValue(memberProperty.Name);

            queryCollectionValue.Elements.Clear();
            foreach (QueryValue queryValue in collectionElements)
            {
                queryCollectionValue.Elements.Add(queryValue);
            }

            instance.SetValue(memberProperty.Name, queryCollectionValue);
        }
Beispiel #27
0
        private static void MarkDynamicSubPropertyValues(QueryStructuralValue value)
        {
            ExceptionUtilities.CheckArgumentNotNull(value, "value");
            var complexType = value.Type as QueryComplexType;

            ExceptionUtilities.CheckObjectNotNull(complexType, "Instance was not a complex type");

            foreach (var memberName in value.MemberNames)
            {
                MarkPropertyAsDynamic(value, memberName);
            }
        }
        /// <summary>
        /// Static method for creating masked structural values. Will not hide any properties initially.
        /// </summary>
        /// <param name="toMask">The structural value to mask</param>
        /// <returns>A masked structural value</returns>
        internal static MaskedQueryStructuralValue Create(QueryStructuralValue toMask)
        {
            ExceptionUtilities.CheckArgumentNotNull(toMask, "toMask");
            var masked = new MaskedQueryStructuralValue(toMask.Type, toMask.IsNull, toMask.EvaluationError, toMask.Type.EvaluationStrategy);
            foreach (var memberName in toMask.MemberNames)
            {
                masked.SetValue(memberName, toMask.GetValue(memberName));
            }

            masked.Annotations.AddRange(toMask.Annotations.Select(a => a.Clone()));

            return masked;
        }
        private static ODataUri GetTopLevelUri(QueryStructuralValue entity)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");
            QueryEntityType entityType = entity.Type as QueryEntityType;

            ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural value was not an entity type");

            var setSegment = ODataUriBuilder.EntitySet(entityType.EntitySet);
            var keyValues  = entityType.EntityType.AllKeyProperties.Select(k => new NamedValue(k.Name, entity.GetScalarValue(k.Name).Value));
            var keySegment = ODataUriBuilder.Key(entityType.EntityType, keyValues);

            return(new ODataUri(setSegment, keySegment));
        }
Beispiel #30
0
        /// <summary>
        /// Fixes up references between entities in the visible entities graph.
        /// </summary>
        protected void FixupReferencesOfVisibleEntities()
        {
            foreach (var entry in this.VisibleEntitiesGraph)
            {
                QueryStructuralValue originalEntity = entry.Key;
                QueryStructuralValue visibleEntity  = entry.Value;

                if (!visibleEntity.IsNull)
                {
                    this.FixupReferencesOfVisibleEntity(originalEntity, visibleEntity);
                }
            }
        }
Beispiel #31
0
        private void UpdateVisibleEntitiesGraphForAnonymousAndGrouping(QueryStructuralValue anonymousOrGroupingValue, string[] spanPaths)
        {
            // we never take Includes into account for anonymous and grouping properties, hence we can terminate/corrupt the current span path
            string newPath = "[anonymous or grouping]";

            if (!anonymousOrGroupingValue.IsNull)
            {
                foreach (QueryProperty property in anonymousOrGroupingValue.Type.Properties)
                {
                    var originalValue = anonymousOrGroupingValue.GetValue(property.Name);
                    this.UpdateVisibleEntitiesGraphForQueryValue(originalValue, newPath, spanPaths);
                }
            }
        }
        private QueryStructuralValue PopulateProperties(QueryStructuralValue structuralToPopulate, object resultFragment)
        {
            foreach (var property in structuralToPopulate.Type.Properties)
            {
                var propertyInfo = resultFragment.GetType().GetProperty(property.Name);
                ExceptionUtilities.CheckObjectNotNull(propertyInfo, "Could not find appropriate property info for the given property: " + property.Name);

                var propertyValue      = propertyInfo.GetValue(resultFragment, null);
                var propertyQueryValue = this.Convert(propertyValue, property.PropertyType);
                structuralToPopulate.SetValue(property.Name, propertyQueryValue);
            }

            return(structuralToPopulate);
        }
        /// <summary>
        /// Static method for creating masked structural values. Will not hide any properties initially.
        /// </summary>
        /// <param name="toMask">The structural value to mask</param>
        /// <returns>A masked structural value</returns>
        internal static MaskedQueryStructuralValue Create(QueryStructuralValue toMask)
        {
            ExceptionUtilities.CheckArgumentNotNull(toMask, "toMask");
            var masked = new MaskedQueryStructuralValue(toMask.Type, toMask.IsNull, toMask.EvaluationError, toMask.Type.EvaluationStrategy);

            foreach (var memberName in toMask.MemberNames)
            {
                masked.SetValue(memberName, toMask.GetValue(memberName));
            }

            masked.Annotations.AddRange(toMask.Annotations.Select(a => a.Clone()));

            return(masked);
        }
        private void CompareComplexBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues)
        {
            int i = 0;

            var collection = instance.GetCollectionValue(memberProperty.Name);

            if (!this.WriteErrorIfNull(propertyPath, collection))
            {
                List<NamedValue> complexInstanceNamedValues = namedValues.Where(pp => pp.Name.StartsWith(propertyPath + "." + i + ".", StringComparison.Ordinal)).ToList();
                while (complexInstanceNamedValues.Any())
                {
                    if (i < collection.Elements.Count)
                    {
                        var complexValue = collection.Elements[i] as QueryStructuralValue;
                        this.CompareValues(complexValue, complexInstanceNamedValues, propertyPath + "." + i);
                    }

                    i++;
                    complexInstanceNamedValues = namedValues.Where(pp => pp.Name.StartsWith(propertyPath + "." + i + ".", StringComparison.Ordinal)).ToList();
                }

                this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath);
            }
        }
        private void CompareBagProperty(QueryStructuralValue instance, QueryProperty memberProperty, QueryType elementType, string propertyPath, IEnumerable<NamedValue> namedValues)
        {
            var scalarElementDataType = elementType as QueryScalarType;
            var complexTypeElementDataType = elementType as QueryComplexType;
     
            if (scalarElementDataType != null)
            {
                this.ComparePrimitiveBag(instance, memberProperty, propertyPath, namedValues);
            }
            else
            {
                ExceptionUtilities.CheckObjectNotNull(complexTypeElementDataType, "PropertyPath '{0}' is an invalid type '{1}'", propertyPath, memberProperty.PropertyType);

                this.CompareComplexBag(instance, memberProperty, propertyPath, namedValues);
            }
        }
        private void CompareValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath)
        {
            foreach (QueryProperty property in instance.Type.Properties)
            {
                string childPropertyPath = property.Name;
                if (propertyPath != null)
                {
                    childPropertyPath = propertyPath + "." + property.Name;
                }

                // Skip if its an EntityType, this only handles structural types
                var queryEntityType = property.PropertyType as QueryEntityType;
                if (queryEntityType != null)
                {
                    continue;
                }

                var collectionType = property.PropertyType as QueryCollectionType;
                var scalarType = property.PropertyType as QueryScalarType;
                var complexDataType = property.PropertyType as QueryComplexType;

                QueryEntityType collectionQueryElementType = null;
                if (collectionType != null)
                {
                    collectionQueryElementType = collectionType.ElementType as QueryEntityType;
                }

                // Skip if its a collection of QueryEntityType
                if (collectionQueryElementType != null)
                {
                    continue;
                }

                if (scalarType != null)
                {
                    // The following code block handles the case where the value is server generated.
                    // For instance, server generated Keys would fail so if a property is marked as being server generated
                    // we make sure to remove that property from the list of properties to verify
                    MemberProperty memberProperty;
                    var instanceEntityType = instance.Type as QueryEntityType;
                    if (instanceEntityType != null)
                    {
                        memberProperty = instanceEntityType.EntityType.AllProperties.SingleOrDefault(p => p.Name == property.Name);
                    }
                    else
                    {
                        memberProperty = ((QueryComplexType)instance.Type).ComplexType.Properties.SingleOrDefault(p => p.Name == property.Name);
                    }

                    if (memberProperty != null && memberProperty.Annotations.OfType<StoreGeneratedPatternAnnotation>().Any())
                    {
                        // TODO: be more fine-grained about whether this is an update or insert (ie, look at the annotation's property values)
                        this.unusedNamedValuePaths.Remove(childPropertyPath);
                        continue;
                    }

                    NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath);
                    if (primitivePropertyNamedValue != null)
                    {
                        var queryValue = instance.GetScalarValue(property.Name);
                        this.WriteErrorIfNotEqual(childPropertyPath, primitivePropertyNamedValue.Value, queryValue);
                        this.unusedNamedValuePaths.Remove(childPropertyPath);
                    }
                }
                else if (collectionType != null)
                {
                    List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList();
                    if (bagNamedValues.Any())
                    {
                        this.CompareBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues);
                    }
                    else
                    {
                        this.CompareBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues);
                    }
                }
                else if (complexDataType != null)
                {
                    // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here
                    QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name);

                    List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList();
                    if (complexInstanceNamedValues.Any())
                    {
                        if (!this.WriteErrorIfNull(childPropertyPath, complexTypeValue))
                        {
                            this.CompareValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath);
                        }
                    }
                    else
                    {
                        // Check for null case
                        List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList();
                        ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath);
                        if (exactMatches.Count == 1)
                        {
                            ExceptionUtilities.Assert(
                                exactMatches[0].Value == null,
                                "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'",
                                childPropertyPath,
                                exactMatches[0].Value);

                            QueryValue queryValue = instance.GetValue(property.Name);
                            this.WriteErrorIfNotNull(childPropertyPath, queryValue);
                            this.unusedNamedValuePaths.Remove(childPropertyPath);
                        }
                    }
                }
            }
        }
        private QueryStructuralValue CloneStructuralValue(Dictionary<QueryStructuralValue, QueryStructuralValue> identityMap, QueryStructuralValue qsv)
        {
            if (qsv.IsNull)
            {
                return qsv;
            }

            QueryStructuralValue clonedValue;

            if (identityMap.TryGetValue(qsv, out clonedValue))
            {
                return clonedValue;
            }

            clonedValue = qsv.Type.CreateNewInstance();
            identityMap.Add(qsv, clonedValue);
      
            foreach (var m in qsv.Type.Properties)
            {
                // copy scalar properties
                if (m.PropertyType is QueryScalarType)
                {
                    clonedValue.SetValue(m.Name, qsv.GetScalarValue(m.Name));
                    continue;
                }

                // copy stream properties
                if (m.PropertyType is AstoriaQueryStreamType)
                {
                    if (m.Name.Contains("DefaultStream"))
                    {
                        clonedValue.SetDefaultStreamValue(qsv.GetDefaultStreamValue());
                    }
                    else
                    {
                        clonedValue.SetStreamValue(m.Name, qsv.GetStreamValue(m.Name));
                    }

                    continue;
                }

                var qst = m.PropertyType as QueryStructuralType;
                if (m.PropertyType is QueryStructuralType)
                {
                    if (!qst.IsValueType)
                    {
                        // skip reference types, clone everything else
                        continue;
                    }

                    clonedValue.SetValue(m.Name, this.CloneStructuralValue(identityMap, qsv.GetStructuralValue(m.Name)));
                }

                var qct = m.PropertyType as QueryCollectionType;
                if (qct != null)
                {
                    var elementStructuralType = qct.ElementType as QueryStructuralType;
                    if (elementStructuralType != null)
                    {
                        if (!elementStructuralType.IsValueType)
                        {
                            // skip collections of reference types, clone everything else
                            continue;
                        }
                    }

                    clonedValue.SetValue(m.Name, this.CloneCollectionValue(identityMap, qsv.GetCollectionValue(m.Name)));
                }
            }

            return clonedValue;
        }
        /// <summary>
        /// Evaluates the given lambda as if it were called on the given entity. Used for evaluating orderby expressions for the last result in a feed.
        /// </summary>
        /// <param name="entity">The entity to evaluate the lambda for.</param>
        /// <param name="lamda">The lamda to evaluate.</param>
        /// <returns>The result of evaluating the lamda for the given entity.</returns>
        private object EvaluateLambdaForEntity(QueryStructuralValue entity, LinqLambdaExpression lamda)
        {
            var expression = CommonQueryBuilder.Root("fakeSet", entity.Type.CreateCollectionType()).Select(lamda).Single();

            var fakeQueryDataSet = new QueryDataSet();
            fakeQueryDataSet.RootQueryData["fakeSet"] = entity.Type.CreateCollectionType().CreateCollectionWithValues(new[] { entity });

            QueryValue evaluated;
            using (this.ExpressionEvaluator.WithTemporaryDataSet(fakeQueryDataSet))
            {
                evaluated = this.ExpressionEvaluator.Evaluate(expression);
            }

            return ((QueryScalarValue)evaluated).Value;
        }
        private string CalculateExpectedETagForEntityOrStream(ODataUri uri, QueryStructuralValue entity)
        {
            if (uri.IsMediaResource())
            {
                return entity.GetDefaultStreamValue().GetExpectedETag();
            }

            if (uri.IsNamedStream())
            {
                var streamSegment = uri.Segments.OfType<NamedStreamSegment>().Last();
                return entity.GetStreamValue(streamSegment.Name).GetExpectedETag();
            }

            return this.LiteralConverter.ConstructWeakETag(entity);
        }
        private static ODataUri GetTopLevelUri(QueryStructuralValue entity)
        {
            ExceptionUtilities.CheckArgumentNotNull(entity, "entity");
            QueryEntityType entityType = entity.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural value was not an entity type");

            var setSegment = ODataUriBuilder.EntitySet(entityType.EntitySet);
            var keyValues = entityType.EntityType.AllKeyProperties.Select(k => new NamedValue(k.Name, entity.GetScalarValue(k.Name).Value));
            var keySegment = ODataUriBuilder.Key(entityType.EntityType, keyValues);

            return new ODataUri(setSegment, keySegment);
        }
        private QueryValue SynchronizeAndEvaluate(ODataUri entityUri, QueryStructuralValue beforeSync, bool synchronizeEntireSet)
        {
            var entityType = beforeSync.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Structural value was not an entity type");

            // if an entity was deleted, synchronize the entire set. Otherwise just synchronize the entity
            if (synchronizeEntireSet)
            {
                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntireEntitySet(c, entityType.EntitySet.Name));
            }
            else
            {
                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, beforeSync));
            }

            return this.Evaluator.Evaluate(entityUri, false, false);
        }
        /// <summary>
        /// Gets the pre and post update representations of the entity updated by the given request
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="beforeUpdate">The entity before the update</param>
        /// <param name="afterUpdate">The entity after the update</param>
        public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests");

            KeyValuePair<QueryStructuralValue, QueryStructuralValue> beforeAndAfter;
            if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter))
            {
                var entityUri = request.Uri.ScopeToEntity();
                var entity = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false);

                beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity));

                afterUpdate = entity;

                beforeAndAfter = new KeyValuePair<QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate);
                this.updatedEntityCache[request] = beforeAndAfter;
            }
            else
            {
                beforeUpdate = beforeAndAfter.Key;
                afterUpdate = beforeAndAfter.Value;
            }
        }
Beispiel #43
0
        /// <summary>
        /// Fixes up the properties of the structural value based on the given selected paths
        /// </summary>
        /// <param name="instance">The value to fix up</param>
        /// <param name="selectedPaths">The selected paths for the value's scope</param>
        /// <returns>The fixed-up value</returns>
        private QueryStructuralValue FixupPropertiesForSelect(QueryStructuralValue instance, IEnumerable<string> selectedPaths)
        {
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");
            ExceptionUtilities.CheckCollectionNotEmpty(selectedPaths, "selectedPaths");

            var entityType = instance.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Select is not supported on non-entity types");

            var masked = MaskedQueryStructuralValue.Create(instance);

            bool wildCard = selectedPaths.Contains(Endpoints.SelectAll);

            var propertyNames = entityType.EntityType.AllProperties.Select(p => p.Name).Concat(entityType.Properties.Streams().Select(m => m.Name));
            foreach (string propertyName in propertyNames)
            {
                if (!selectedPaths.Contains(Uri.EscapeDataString(propertyName)) && !wildCard)
                {
                    // Primitive, bag and stream properties are either entirely present or entirely missing.
                    // However, complex properties can be partially present if a mapped sub-property has a
                    // null value. For this reason, we recursively hide the property and all sub-properties.
                    HidePropertyRecursive(masked, propertyName);
                }
            }

            foreach (string propertyName in entityType.EntityType.AllNavigationProperties.Select(p => p.Name))
            {
                string pathMarker = Uri.EscapeDataString(propertyName) + "/";
                var subpaths = selectedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length));

                var value = instance.GetValue(propertyName);
                ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName);

                if (selectedPaths.Contains(Uri.EscapeDataString(propertyName)))
                {
                    continue;
                }
                else if (subpaths.Any())
                {
                    var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, subpaths));
                    masked.SetValue(propertyName, maskedValue);
                }
                else if (wildCard)
                {
                    masked.SetValue(propertyName, value.Type.NullValue);
                }
                else
                {
                    masked.HideMember(propertyName);
                }
            }

            return masked;
        }
Beispiel #44
0
        /// <summary>
        /// Fixes up the properties of the structural value based on the given expanded paths
        /// </summary>
        /// <param name="instance">The value to fix up</param>
        /// <param name="expandedPaths">The expanded paths for the value's scope</param>
        /// <returns>The fixed-up value</returns>
        private QueryStructuralValue FixupPropertiesForExpand(QueryStructuralValue instance, IEnumerable<string> expandedPaths)
        {
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");
            ExceptionUtilities.CheckArgumentNotNull(expandedPaths, "expandedPaths");

            var entityType = instance.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Expand is not supported on non-entity types");

            var masked = MaskedQueryStructuralValue.Create(instance);

            foreach (var navigation in entityType.EntityType.AllNavigationProperties)
            {
                string expandedPath;
                string propertyName = navigation.Name;
                var value = instance.GetValue(propertyName);
                
                ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName);

                bool match = TryGetExpandedPath(expandedPaths, propertyName, entityType.EntityType, out expandedPath);
                string pathMarker = expandedPath + "/";
                var subpaths = expandedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length));

                // note that if there is match, we still need to fix up up any children, even if there are no subpaths
                if (subpaths.Any() || match)
                {
                    // recurse
                    var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, subpaths));

                    // handle page limit on expanded collections
                    if (maskedValue.Type is QueryCollectionType)
                    {
                        var collection = maskedValue as QueryCollectionValue;
                        ExceptionUtilities.CheckObjectNotNull(collection, "Value was a collection type, but not a collection value");

                        var relatedSet = entityType.EntitySet.GetRelatedEntitySet(navigation);
                        var relatedSetPageSize = relatedSet.GetEffectivePageSize();
                        if (relatedSetPageSize.HasValue && this.applyPagingInExpands)
                        {
                            collection = this.AddOrderingForPagingToQueryCollectionValue(collection);
                            maskedValue = collection.Take(relatedSetPageSize.Value);
                        }
                    }

                    masked.SetValue(propertyName, maskedValue);
                }
                else if (!match)
                {
                    // set any non-expanded navigation properties to null
                    masked.SetValue(propertyName, value.Type.NullValue);
                }
            }

            return masked;
        }
        private void ComparePrimitiveBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues)
        {
            int i = 0;

            var collection = instance.GetCollectionValue(memberProperty.Name);

            if (!this.WriteErrorIfNull(propertyPath, collection))
            {
                List<NamedValue> primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList();
                while (primitiveItemNamedValues.Any())
                {                    
                    if (i < collection.Elements.Count)
                    {
                        var scalarQueryValue = collection.Elements[i] as QueryScalarValue;
                        ExceptionUtilities.Assert(primitiveItemNamedValues.Count() < 2, "Should not get more than one value for a primitive Bag item for path '{0}'", propertyPath + "." + i);
                        var value = primitiveItemNamedValues.Single();
                        this.WriteErrorIfNotEqual(propertyPath, value.Value, scalarQueryValue);
                        this.unusedNamedValuePaths.Remove(value.Name);
                    }

                    i++;
                    primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList();
                }

                this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath);
            }
        }
        private void VerifyTypeNameForInsert(ODataRequest request, ODataResponse response, QueryStructuralValue inserted)
        {
            var entityInstance = request.Body.RootElement as EntityInstance;
            ExceptionUtilities.CheckObjectNotNull(entityInstance, "Cannot get expected type name because request payload was not an entity instance. It was: '{0}'", request.Body.RootElement.ElementType);

            string expectedTypeName = request.Uri.Segments.OfType<EntityTypeSegment>().Select(t => t.EntityType.FullName).LastOrDefault();
            if (entityInstance.FullTypeName != null)
            {
                expectedTypeName = entityInstance.FullTypeName;
            }
            
            var entityType = inserted.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Cannot verify type because inserted value is not an entity type. Type was: '{0}'.", inserted.Type);

            this.AssertAreEqual(expectedTypeName, entityType.EntityType.FullName, "Inserted instance type did not match", request, response);
        }
        private void CompareBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues)
        {
            List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList();

            if (!exactMatches.Any())
            {
                return;
            }

            ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath);
            
            QueryCollectionValue actualQueryBagValue = instance.GetCollectionValue(memberProperty.Name);
            NamedValue expectedBagValue = exactMatches.Single();

            if (expectedBagValue.Value == null)
            {
                this.WriteErrorIfNotNull(propertyPath, actualQueryBagValue);
                this.unusedNamedValuePaths.Remove(propertyPath);
            }
            else if (expectedBagValue.Value == EmptyData.Value)
            {
                if (!this.WriteErrorIfNull(propertyPath, actualQueryBagValue))
                {
                    this.WriteErrorIfNotEqual(propertyPath, actualQueryBagValue.Elements.Count, 0, "Expected zero elements in BagProperty");
                    this.unusedNamedValuePaths.Remove(propertyPath);
                }
            }
        }
        private void VerifyResponsePayload(ODataRequest request, ODataResponse response, QueryStructuralValue expected)
        {
            if (response.StatusCode != HttpStatusCode.NoContent)
            {
                QueryValue expectedValue = expected;
                if (!request.Uri.IsEntity())
                {
                    var entityUri = request.Uri.ScopeToEntity();
                    foreach (var property in request.Uri.Segments.Skip(entityUri.Segments.Count).OfType<PropertySegment>())
                    {
                        expectedValue = ((QueryStructuralValue)expectedValue).GetValue(property.Property.Name);
                    }
                }

                this.VerificationServices.ValidateResponsePayload(request.Uri, response, expectedValue, this.maxProtocolVersion);
            }
        }
        /// <summary>
        /// Factory method to create the <see cref="LinqToAstoriaKeyExpression"/>.
        /// </summary>
        /// <param name="source">The source query.</param>
        /// <param name="instance">The instance to get key values from.</param>
        /// <returns>The <see cref="QueryExpression"/> with a key matching the values of the given instance.</returns>
        public static LinqToAstoriaKeyExpression Key(this QueryExpression source, QueryStructuralValue instance)
        {
            ExceptionUtilities.CheckArgumentNotNull(source, "source");
            ExceptionUtilities.CheckArgumentNotNull(instance, "instance");

            var entityType = instance.Type as QueryEntityType;
            ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural instance was not an entity. Type was: {0}", instance.Type);

            var key = new List<KeyValuePair<QueryProperty, QueryConstantExpression>>();
            foreach (var keyProperty in entityType.EntityType.AllKeyProperties)
            {
                var queryProperty = entityType.Properties.SingleOrDefault(p => p.Name == keyProperty.Name);
                ExceptionUtilities.CheckObjectNotNull(queryProperty, "Could not find property with name '{0}' on type '{1}'", keyProperty.Name, entityType);

                var value = instance.GetScalarValue(keyProperty.Name);

                key.Add(new KeyValuePair<QueryProperty, QueryConstantExpression>(queryProperty, CommonQueryBuilder.Constant(value)));
            }

            return new LinqToAstoriaKeyExpression(source, key, entityType.CreateCollectionType());
        }
 private void GetUpdatedEntityBeforeAndAfter(ODataRequest request, ODataResponse response, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
 {
     using (this.Context.Begin(request))
     {
         if (request.GetEffectiveVerb() == HttpVerb.Post)
         {
             afterUpdate = this.Context.GetInsertedEntity(request, response);
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "Structural value returned by GetInsertedEntity unexpectedly null");
             beforeUpdate = afterUpdate.Type.NullValue;
         }
         else
         {
             this.Context.GetUpdatedEntity(request, out beforeUpdate, out afterUpdate);
             ExceptionUtilities.CheckObjectNotNull(beforeUpdate, "Before-update structural value returned by GetUpdatedEntity unexpectedly null");
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "After-update structural value returned by GetUpdatedEntity unexpectedly null");
         }
     }
 }
        /// <summary>
        /// Generates the next link for an expanded feed.
        /// </summary>
        /// <param name="containingEntity">The containing entity.</param>
        /// <param name="navigation">The expanded navigation property.</param>
        /// <param name="lastEntityValue">The last entity value.</param>
        /// <returns>
        /// The expected next link
        /// </returns>
        public string GenerateExpandedNextLink(EntityInstance containingEntity, NavigationPropertyInstance navigation, QueryStructuralValue lastEntityValue)
        {
            ExceptionUtilities.CheckArgumentNotNull(containingEntity, "containingEntity");
            ExceptionUtilities.CheckArgumentNotNull(navigation, "navigation");
            ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue");

            var navigationUriString = ((ExpandedLink)navigation.Value).UriString;
            if (string.IsNullOrEmpty(navigationUriString))
            {
                navigationUriString = UriHelpers.ConcatenateUriSegments(containingEntity.EditLink, navigation.Name);
            }

            var skipTokenValues = new List<object>();
            foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey))
            {
                skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value);
            }

            var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues);

            var nextLinkUri = new ODataUri(new UnrecognizedSegment(navigationUriString));
            nextLinkUri.SkipToken = skiptoken;

            return this.UriToStringConverter.ConvertToString(nextLinkUri);
        }
            /// <summary>
            /// Verify ETag and Id values of given payload element.
            /// </summary>
            /// <param name="entityType">type of the element</param>
            /// <param name="payloadElement">payload element to verify</param>
            /// <param name="value">expected values</param>
            private void VerifyEntityMetadata(QueryEntityType entityType, EntityInstance payloadElement, QueryStructuralValue value)
            {
                if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeETags))
                {
                    if (entityType.EntityType.HasETag())
                    {
                        // TODO: are ETags always based on property values?
                        var expectedETag = this.parent.LiteralConverter.ConstructWeakETag(value);
                        this.parent.Assert.AreEqual(expectedETag, payloadElement.ETag, "Entity's ETag did not match");
                    }
                    else
                    {
                        this.parent.Assert.IsNull(payloadElement.ETag, "Entity should not have had an ETag");
                    }
                }

                if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeEntityIdentifier))
                {
                    this.parent.Assert.IsNotNull(payloadElement.Id, "Entity's ID unexpectedly null");

                    if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.UseConventionBasedIdentifiers))
                    {
                        var expectedId = this.parent.LinkGenerator.GenerateEntityId(value);

                        this.parent.Assert.AreEqual(expectedId, payloadElement.Id, "Entity's ID did not match");
                    }
                }
                else
                {
                    this.parent.Assert.IsNull(payloadElement.Id, "Entity's ID unexpectedly non-null");
                }

                if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.UseConventionBasedLinks))
                {
                    var expectedEditLink = this.parent.LinkGenerator.GenerateEntityEditLink(value);
                    this.CompareUri(expectedEditLink, payloadElement.EditLink, "Entity's edit-link did not match");
                }

                if (entityType.EntityType.GetBaseTypesAndSelf().Any(t => t.HasStream()))
                {
                    using (this.parent.Assert.WithMessage("Entity's stream metadata did not match"))
                    {
                        var defaultStreamValue = value.GetDefaultStreamValue();
                        this.parent.Assert.AreEqual(defaultStreamValue.ContentType, payloadElement.StreamContentType, "Content type did not match");

                        this.CompareStreamETag(defaultStreamValue, payloadElement.StreamETag);

                        if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeMediaResourceEditLinks))
                        {
                            this.CompareUri(defaultStreamValue.EditLink, payloadElement.StreamEditLink, "Edit link did not match");
                        }
                        else
                        {
                            this.parent.Assert.IsNull(payloadElement.StreamEditLink, "Edit link unexpectedly non-null");
                        }

                        if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeMediaResourceSourceLinks))
                        {
                            this.CompareUri(defaultStreamValue.SelfLink, payloadElement.StreamSourceLink, "Source link did not match");
                        }
                        else
                        {
                            this.parent.Assert.IsNull(payloadElement.StreamSourceLink, "Source link unexpectedly non-null");
                        }
                    }
                }
            }
        /// <summary>
        /// Generates the expected next link for a top-level feed
        /// </summary>
        /// <param name="requestUri">The request URI.</param>
        /// <param name="pageSize">The page size.</param>
        /// <param name="lastEntityValue">The last entity value.</param>
        /// <returns>
        /// The expected next link
        /// </returns>
        public string GenerateNextLink(ODataUri requestUri, int pageSize, QueryStructuralValue lastEntityValue)
        {
            ExceptionUtilities.CheckArgumentNotNull(requestUri, "requestUri");
            ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue");
            
            var queryBasedUri = requestUri as QueryBasedODataUri;
            ExceptionUtilities.CheckObjectNotNull(queryBasedUri, "Only uris which were generated from queries are supported");
            
            var skipTokenValues = new List<object>();
            foreach (var keySelector in queryBasedUri.OrderByExpressions.SelectMany(o => o.KeySelectors))
            {
                skipTokenValues.Add(this.EvaluateLambdaForEntity(lastEntityValue, keySelector));
            }

            foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey))
            {
                skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value);
            }

            var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues);

            // copy request uri segments exactly
            var nextLinkUri = new ODataUri(requestUri.Segments);

            // copy over $orderby, $filter, $expand, $select, and $inlinecount
            nextLinkUri.OrderBy = ModifyQueryOptionToMatchProduct(requestUri.OrderBy);
            nextLinkUri.Filter = ModifyQueryOptionToMatchProduct(requestUri.Filter);
            nextLinkUri.ExpandSegments = requestUri.ExpandSegments;
            nextLinkUri.SelectSegments = requestUri.SelectSegments;

            string inlineCountValue;
            if (requestUri.TryGetInlineCountValue(out inlineCountValue))
            {
                nextLinkUri.InlineCount = inlineCountValue;
            }

            // add the $skiptoken generated above
            nextLinkUri.SkipToken = skiptoken;

            // generate a new $top value
            if (requestUri.Top.HasValue)
            {
                var top = requestUri.Top.Value - pageSize;
                if (top > 0)
                {
                    nextLinkUri.Top = top;
                }
            }

            if (requestUri.IsServiceOperation())
            {
                var functionSegment = requestUri.Segments.OfType<FunctionSegment>().Last();
                foreach (var paramName in functionSegment.Function.Parameters.Select(p => p.Name))
                {
                    string paramValue;
                    if (requestUri.CustomQueryOptions.TryGetValue(paramName, out paramValue))
                    {
                        nextLinkUri.CustomQueryOptions[paramName] = paramValue;
                    }
                }
            }

            return this.UriToStringConverter.ConvertToString(nextLinkUri);
        }
        internal void UpdateValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath)
        {
            foreach (QueryProperty property in instance.Type.Properties)
            {
                string childPropertyPath = property.Name;
                if (propertyPath != null)
                {
                    childPropertyPath = propertyPath + "." + property.Name;
                }

                // Skip if its an EntityType, this only handles structural types
                var queryEntityType = property.PropertyType as QueryEntityType;
                if (queryEntityType != null)
                {
                    continue;
                }

                var collectionType = property.PropertyType as QueryCollectionType;
                var scalarType = property.PropertyType as QueryScalarType;
                var complexDataType = property.PropertyType as QueryComplexType;

                QueryEntityType collectionQueryElementType = null;
                if (collectionType != null)
                {
                    collectionQueryElementType = collectionType.ElementType as QueryEntityType;
                }

                // Skip if its a collection of QueryEntityType
                if (collectionQueryElementType != null)
                {
                    continue;
                }

                if (scalarType != null)
                {
                    NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath);
                    if (primitivePropertyNamedValue != null)
                    {
                        instance.SetPrimitiveValue(property.Name, primitivePropertyNamedValue.Value);
                        this.unusedNamedValuePaths.Remove(childPropertyPath);
                    }
                }
                else if (collectionType != null)
                {
                    List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList();
                    if (bagNamedValues.Any())
                    {
                        this.UpdateBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues);
                    }
                    else
                    {
                        this.UpdateBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues);
                    }
                }
                else if (complexDataType != null)
                {
                    // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here
                    QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name);

                    List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList();
                    if (complexInstanceNamedValues.Any())
                    {
                        if (complexTypeValue.IsNull)
                        {
                            complexTypeValue = complexDataType.CreateNewInstance();
                        }

                        this.UpdateValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath);
                        instance.SetValue(property.Name, complexTypeValue);
                    }
                    else
                    {
                        // Check for null case
                        List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList();
                        ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath);
                        if (exactMatches.Count == 1)
                        {
                            ExceptionUtilities.Assert(
                                exactMatches[0].Value == null, 
                                "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", 
                                childPropertyPath,
                                exactMatches[0].Value);

                            instance.SetValue(property.Name, complexDataType.NullValue);
                            this.unusedNamedValuePaths.Remove(childPropertyPath);
                        }
                    }
                }
            }
        }
 private void VerifyPropertyValues(ODataRequest request, IQueryScalarValueToClrValueComparer primitiveComparer, QueryStructuralValue afterUpdate)
 {
     var namedValues = this.ExtractNamedValues(request);
     this.Comparer.Compare(afterUpdate, namedValues, primitiveComparer);
 }
Beispiel #56
0
        private void QueryEntityInstanceAndGenerateStreams(QueryStructuralValue queryStructuralValue, IAsyncContinuation continuation)
        {
            var rootQuery = (QueryExpression)this.queryStructuralValueToRootQueryLookup[queryStructuralValue];
            var query = ODataQueryTestCase.GetExistingEntityQuery(rootQuery, queryStructuralValue);
            var requestUriString = this.QueryExpressionToUriConverter.ComputeUri(query);
            requestUriString = UriHelpers.ConcatenateUriSegments(this.Workspace.ServiceUri.OriginalString, requestUriString);

            HttpWebRequest requestForExistingEntity = (HttpWebRequest)HttpWebRequest.Create(requestUriString);

            requestForExistingEntity.GetResponse<HttpWebResponse>(
                this.Asynchronous,
                continuation,
                (response) =>
                {
                    ProcessEntityAndGenerateStreams(queryStructuralValue, continuation, response);
                });
        }
        private void VerifyStoreData(ODataRequest request, ODataResponse response, QueryStructuralValue storeValue)
        {
            string contentType;
            ExceptionUtilities.Assert(request.Headers.TryGetValue(HttpHeaders.ContentType, out contentType), "Could not get Content-Type header from request");

            if (request.Uri.IsNamedStream())
            {
                string streamName = request.Uri.Segments.OfType<NamedStreamSegment>().Last().Name;
                var streamValue = storeValue.GetStreamValue(streamName);
                this.VerifyStream(streamValue, contentType, request, response);
                return;
            }

            bool isInsert = request.GetEffectiveVerb() == HttpVerb.Post;
            bool isMediaResource = request.Uri.IsMediaResource();
            if (isInsert)
            {
                EntitySet expectedEntitySet;
                if (request.Uri.TryGetExpectedEntitySet(out expectedEntitySet))
                {
                    isMediaResource = expectedEntitySet.EntityType.HasStream();
                }
            }

            if (isMediaResource)
            {
                var streamValue = storeValue.GetDefaultStreamValue();
                this.VerifyStream(streamValue, contentType, request, response);
            }
            else
            {
                if (isInsert)
                {
                    this.VerifyTypeNameForInsert(request, response, storeValue);
                }

                var formatStrategy = this.FormatSelector.GetStrategy(contentType, request.Uri);
                var primitiveComparer = formatStrategy.GetPrimitiveComparer();

                // TODO: verify relationships
                // TODO: verify PUT vs PATCH semantics
                this.VerifyPropertyValues(request, primitiveComparer, storeValue);
            }
        }
Beispiel #58
0
        private void ProcessEntityAndGenerateStreams(QueryStructuralValue queryStructuralValue, IAsyncContinuation continuation, HttpWebResponse response)
        {
            ExceptionUtilities.Assert(response.StatusCode == HttpStatusCode.OK, "Error generating stream data, response code incorrect:" + response.StatusCode.ToString());
            var responseValue = new StreamReader(response.GetResponseStream()).ReadToEnd();

            try
            {
                var existingEntityInXML = XElement.Parse(responseValue);
                var feedInstance = this.XmlToPayloadConverter.ConvertToPayloadElement(existingEntityInXML) as EntitySetInstance;
                ExceptionUtilities.CheckObjectNotNull(feedInstance, "Error generating stream data, cannot deserialize response:" + existingEntityInXML);

                var type = queryStructuralValue.Type as QueryEntityType;
                ExceptionUtilities.CheckObjectNotNull(type, "Type was not an entity type. Type was {0}", type.StringRepresentation);

                Func<AstoriaQueryStreamValue, bool> valueFilter = v => v.IsNull || v.Value.Length == 0;
                Func<QueryProperty, bool> propertyFilter = p => p.IsStream() && valueFilter(queryStructuralValue.GetStreamValue(p.Name));
                var streamPropertiesToUpdate = type.Properties.Where(propertyFilter).ToList();

                if (feedInstance.Count != 0)
                {
                    var entityInstance = feedInstance.SingleOrDefault();
                    ExceptionUtilities.CheckObjectNotNull(entityInstance, "Payload did not contain a single entity instance");

                    var baseAddressAnnotation = feedInstance.Annotations.OfType<XmlBaseAnnotation>().SingleOrDefault();

                    foreach (var streamProperty in streamPropertiesToUpdate)
                    {
                        var streamPropertyType = streamProperty.PropertyType as AstoriaQueryStreamType;
                        ExceptionUtilities.CheckObjectNotNull(streamPropertyType, "PropertyType is not an AstoriaQueryStreamType!", streamProperty.PropertyType);

                        var streamData = this.GenerateStreamData(entityInstance, baseAddressAnnotation, streamProperty);
                        this.streamsToUpdate.Add(streamData);
                    }
                }

                continuation.Continue();
            }
            catch (XmlException)
            {
                this.Logger.WriteLine(LogLevel.Error, "Error in Xml payload:" + responseValue);

                throw;
            }
        }
        private void UpdateBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues)
        {
            List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList();
            if (!exactMatches.Any())
            {
                return;
            }

            ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath);

            NamedValue expectedBagValue = exactMatches.Single();
            var bagType = memberProperty.PropertyType as QueryCollectionType;

            if (expectedBagValue.Value == null)
            {
                instance.SetValue(memberProperty.Name, bagType.NullValue);
                this.unusedNamedValuePaths.Remove(expectedBagValue.Name);
            }
            else if (expectedBagValue.Value == EmptyData.Value)
            {
                QueryCollectionValue value = bagType.CreateCollectionWithValues(new QueryValue[] { });
                instance.SetValue(memberProperty.Name, value);
                this.unusedNamedValuePaths.Remove(expectedBagValue.Name);
            }
        }
            private void VisitStructuralInstance(ComplexInstance payloadElement, QueryStructuralValue value)
            {
                ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
                ExceptionUtilities.CheckArgumentNotNull(value, "value");

                var entityType = value.Type as QueryEntityType;
                var complexType = value.Type as QueryComplexType;
                ExceptionUtilities.Assert(entityType != null || complexType != null, "Value was neither an entity type nor a complex type");

                bool isEntity = entityType != null;
                string errorType = isEntity ? "Entity" : "Complex";

                if (value.IsNull)
                {
                    this.parent.Assert.IsTrue(payloadElement.IsNull, errorType + " instance unexpectedly non-null");
                    return;
                }
                else
                {
                    this.parent.Assert.IsFalse(payloadElement.IsNull, errorType + " instance unexpectedly null");

                    this.VerifyTypeName(value, payloadElement.FullTypeName, errorType + " instance type name did not match expectation.");
                }

                // get all the payload properties, and remove them as we go to detect any extras
                var payloadProperties = payloadElement.Properties.ToList();

                // this is data-driven to deal with open types, but we need to skip over the 'default stream' property if it exists
                foreach (var propertyName in value.MemberNames.Where(m => m != AstoriaQueryStreamType.DefaultStreamPropertyName))
                {
                    var propertyInstance = payloadProperties.SingleOrDefault(p => p.Name == propertyName);
                    this.parent.Assert.IsNotNull(propertyInstance, string.Format(CultureInfo.InvariantCulture, "Could not find property '{0}' in payload", propertyName));
                    payloadProperties.Remove(propertyInstance);

                    var propertyValue = value.GetValue(propertyName);
                    this.RecurseWithMessage(propertyInstance, propertyValue, "{0} instance did not match expectation", errorType);
                }

                string extraPropertyNames = string.Join(", ", payloadProperties.Select(p => '\'' + p.Name + '\'').ToArray());
                this.parent.Assert.IsTrue(payloadProperties.Count == 0, string.Format(CultureInfo.InvariantCulture, "{0} instance contained unexpected properties: {1}", errorType, extraPropertyNames));
            }