public void DefaultValuesTest()
 {
     ODataNavigationLink navigationLink = new ODataNavigationLink();
     this.Assert.IsNull(navigationLink.Name, "Expected null default value for property 'Name'.");
     this.Assert.IsNull(navigationLink.Url, "Expected null default value for property 'Url'.");
     this.Assert.IsNull(navigationLink.IsCollection, "Expected null default value for property 'IsCollection'.");
 }
        public void InitTest()
        {
            this.navigationLink = new ODataNavigationLink();

            var entry = new ODataEntry
            {
                TypeName = "ns.DerivedType",
                Properties = new[]
                {
                    new ODataProperty{Name = "Id", Value = 1, SerializationInfo = new ODataPropertySerializationInfo{PropertyKind = ODataPropertyKind.Key}},
                    new ODataProperty{Name = "Name", Value = "Bob", SerializationInfo = new ODataPropertySerializationInfo{PropertyKind = ODataPropertyKind.ETag}}
                }
            };

            var serializationInfo = new ODataFeedAndEntrySerializationInfo { NavigationSourceName = "Set", NavigationSourceEntityTypeName = "ns.BaseType", ExpectedTypeName = "ns.BaseType" };
            var typeContext = ODataFeedAndEntryTypeContext.Create(serializationInfo, null, null, null, EdmCoreModel.Instance, true);
            var metadataContext = new TestMetadataContext();
            var entryMetadataContext = ODataEntryMetadataContext.Create(entry, typeContext, serializationInfo, null, metadataContext, SelectedPropertiesNode.EntireSubtree);
            var metadataBuilder = new ODataConventionalEntityMetadataBuilder(entryMetadataContext, metadataContext, new ODataConventionalUriBuilder(ServiceUri, UrlConvention.CreateWithExplicitValue(false)));
            this.navigationLinkWithFullBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithFullBuilder.MetadataBuilder = metadataBuilder;

            this.navigationLinkWithNoOpBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithNoOpBuilder.MetadataBuilder = new NoOpEntityMetadataBuilder(entry);

            this.navigationLinkWithNullBuilder = new ODataNavigationLink { Name = "NavProp" };
            this.navigationLinkWithNullBuilder.MetadataBuilder = ODataEntityMetadataBuilder.Null;
        }
 /// <summary>
 /// Creates the materializer link with a feed.
 /// </summary>
 /// <param name="link">The link.</param>
 /// <param name="feed">The feed.</param>
 /// <returns>The materializer link.</returns>
 public static MaterializerNavigationLink CreateLink(ODataNavigationLink link, ODataFeed feed)
 {
     Debug.Assert(link.GetAnnotation<MaterializerNavigationLink>() == null, "there should be no MaterializerNavigationLink annotation on the feed link yet");
     MaterializerNavigationLink materializedNavigationLink = new MaterializerNavigationLink(link, feed);
     link.SetAnnotation<MaterializerNavigationLink>(materializedNavigationLink);
     return materializedNavigationLink;
 }
 /// <summary>
 /// Prevents a default instance of the <see cref="MaterializerNavigationLink"/> struct from being created.
 /// </summary>
 /// <param name="link">The link.</param>
 /// <param name="materializedFeedOrEntry">Value of the link.</param>
 private MaterializerNavigationLink(ODataNavigationLink link, object materializedFeedOrEntry)
 {
     Debug.Assert(link != null, "link != null");
     Debug.Assert(materializedFeedOrEntry != null, "materializedFeedOrEntry != null");
     Debug.Assert(materializedFeedOrEntry is MaterializerEntry || materializedFeedOrEntry is ODataFeed, "must be feed or entry");
     this.link = link;
     this.feedOrEntry = materializedFeedOrEntry;
 }
 /// <summary>
 /// Creates a new instance of DataServiceODataWriterNavigationLinkArgs.
 /// </summary>
 /// <param name="navigationLink">Instance of ODataNavigationLink.</param>
 /// <param name="operationContext">Instance of DataServiceOperationContext.</param>
 public DataServiceODataWriterNavigationLinkArgs(
     ODataNavigationLink navigationLink,
     DataServiceOperationContext operationContext)
 {
     WebUtil.CheckArgumentNull(navigationLink, "navigationLink != null");
     Debug.Assert(operationContext != null, "navigationLink != null");
     this.NavigationLink = navigationLink;
     this.OperationContext = operationContext;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="WritingNavigationLinkArgs"/> class.
 /// </summary>
 /// <param name="link">The link.</param>
 /// <param name="source">The source.</param>
 /// <param name="target">The target.</param>
 public WritingNavigationLinkArgs(ODataNavigationLink link, object source, object target)
 {
     Util.CheckArgumentNull(link, "link");
     Util.CheckArgumentNull(source, "source");
     Util.CheckArgumentNull(target, "target");
     this.Link = link;
     this.Source = source;
     this.Target = target;
 }
            protected override void  VisitNavigationLink(ODataNavigationLink navigationLink)
            {
                bool? expectedIsCollectionValue;
                if (this.expectedIsCollectionValues != null && this.expectedIsCollectionValues.TryGetValue(navigationLink.Name, out expectedIsCollectionValue))
                {
                    this.assertionHandler.AreEqual(
                        expectedIsCollectionValue,
                        navigationLink.IsCollection,
                        "Value for IsCollection on NavigationLink '{0}' is unexpected",
                        navigationLink.Name);
                }

                base.VisitNavigationLink(navigationLink);
            }
Beispiel #8
0
        /// <summary>Checks that for duplicate association links and if there already is a navigation link with the same name
        /// sets the association link URL on that navigation link.</summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the current scope.</param>
        /// <param name="associationLinkName">The name of association link to be checked.</param>
        /// <param name="associationLinkUrl">The url of association link to be checked.</param>
        internal static void CheckForDuplicateAssociationLinkAndUpdateNavigationLink(
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            string associationLinkName,
            Uri associationLinkUrl)
        {
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            Debug.Assert(associationLinkName != null, "associationLinkName != null");

            ODataNavigationLink navigationLink = duplicatePropertyNamesChecker.CheckForDuplicateAssociationLinkNames(associationLinkName, associationLinkUrl);

            // We must not set the AssociationLinkUrl to null since that would disable templating on it, but we want templating to work if the association link was not in the payload.
            if (navigationLink != null && navigationLink.AssociationLinkUrl == null && associationLinkUrl != null)
            {
                navigationLink.AssociationLinkUrl = associationLinkUrl;
            }
        }
        public void ValidateShortIntegrationFeedReading()
        {
            var initialFeed = new ODataFeed() {Id = new Uri("http://services.odata.org/OData/OData.svc/Products")};
            
            var productItem = new ODataEntry() {Id = new Uri("http://services.odata.org/OData/OData.svc/Products(0)")};
            productItem.Properties = new ODataProperty[] {new ODataProperty() {Name = "Id", Value = 0}};

            var categoryNavigationLink = new ODataNavigationLink() {Name = "Category"};

            var categoryItem = new ODataEntry() { Id = new Uri("http://services.odata.org/OData/OData.svc/Categories(0)") };
            categoryItem.Properties = new ODataProperty[] { new ODataProperty() { Name = "Id", Value = 0 } };

            var productsNavigationLink = new ODataNavigationLink() { Name = "Products" };

            var supplierNavigationLink = new ODataNavigationLink() { Name = "Supplier" };

            var testODataReader = new TestODataReader()
            {
               new TestODataReaderItem(ODataReaderState.FeedStart, initialFeed),
               new TestODataReaderItem(ODataReaderState.EntryStart, productItem),
               new TestODataReaderItem(ODataReaderState.NavigationLinkStart, categoryNavigationLink),
               new TestODataReaderItem(ODataReaderState.EntryStart, categoryItem),
               new TestODataReaderItem(ODataReaderState.NavigationLinkStart, productsNavigationLink),
               new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, productsNavigationLink),
               new TestODataReaderItem(ODataReaderState.EntryEnd, categoryItem),
               new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, categoryNavigationLink),
               new TestODataReaderItem(ODataReaderState.NavigationLinkStart, supplierNavigationLink),
               new TestODataReaderItem(ODataReaderState.NavigationLinkEnd, supplierNavigationLink),
               new TestODataReaderItem(ODataReaderState.EntryEnd, productItem),
               new TestODataReaderItem(ODataReaderState.FeedEnd, initialFeed),
            };

            ClientEdmModel clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4);

            var responsePipeline = new DataServiceClientResponsePipelineConfiguration(new DataServiceContext());
            var odataReaderWrapper = ODataReaderWrapper.CreateForTest(testODataReader, responsePipeline);
            FeedAndEntryMaterializerAdapter reader = new FeedAndEntryMaterializerAdapter(ODataFormat.Atom, odataReaderWrapper, clientEdmModel, MergeOption.OverwriteChanges);

            int readCounter = 0;

            while (reader.Read())
            {
                readCounter++;
            }

            readCounter.Should().Be(2);
        }
        public void PropertySettersNullTest()
        {
            ODataNavigationLink navigationLink = new ODataNavigationLink()
                {
                    Name = "NewLink",
                    Url = new Uri("http://odata.org"),
                    IsCollection = true,
                };

            navigationLink.Name = null;
            navigationLink.Url = null;
            navigationLink.IsCollection = null;

            this.Assert.IsNull(navigationLink.Name, "Expected null value for property 'Name'.");
            this.Assert.IsNull(navigationLink.Url, "Expected null value for property 'Url'.");
            this.Assert.IsNull(navigationLink.Url, "Expected null value for property 'IsCollection'.");
        }
        public void PropertyGettersAndSettersTest()
        {
            string name = "ODataNavigationLink";
            Uri url = new Uri("http://odatatest.org/");
            bool isCollection = true;

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = name,
                Url = url,
                IsCollection = isCollection,
            };

            this.Assert.AreEqual(name, navigationLink.Name, "Expected equal name values.");
            this.Assert.AreSame(url, navigationLink.Url, "Expected reference equal values for property 'Url'.");
            this.Assert.AreEqual(isCollection, navigationLink.IsCollection, "Expected equal values for property 'IsCollection'");
        }
Beispiel #12
0
        /// <summary>Checks for duplicate navigation links and if there already is an association link with the same name
        /// sets the association link URL on the navigation link.</summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the current scope.</param>
        /// <param name="navigationLink">The navigation link to be checked.</param>
        /// <param name="isExpanded">true if the link is expanded, false otherwise.</param>
        /// <param name="isCollection">true if the navigation link is a collection, false if it's a singleton or null if we don't know.</param>
        internal static void CheckForDuplicateNavigationLinkNameAndSetAssociationLink(
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ODataNavigationLink navigationLink,
            bool isExpanded,
            bool?isCollection)
        {
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            Debug.Assert(navigationLink != null, "navigationLink != null");

            Uri associationLinkUrl = duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(navigationLink, isExpanded, isCollection);

            // We must not set the AssociationLinkUrl to null since that would disable templating on it, but we want templating to work if the association link was not in the payload.
            if (associationLinkUrl != null && navigationLink.AssociationLinkUrl == null)
            {
                navigationLink.AssociationLinkUrl = associationLinkUrl;
            }
        }
Beispiel #13
0
        /// <summary>Checks for duplicate navigation links and if there already is an association link with the same name
        /// sets the association link URL on the navigation link.</summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the current scope.</param>
        /// <param name="navigationLink">The navigation link to be checked.</param>
        /// <param name="isExpanded">true if the link is expanded, false otherwise.</param>
        /// <param name="isCollection">true if the navigation link is a collection, false if it's a singleton or null if we don't know.</param>
        internal static void CheckForDuplicateNavigationLinkNameAndSetAssociationLink(
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ODataNavigationLink navigationLink,
            bool isExpanded,
            bool? isCollection)
        {
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            Debug.Assert(navigationLink != null, "navigationLink != null");
            
            Uri associationLinkUrl = duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(navigationLink, isExpanded, isCollection);

            // We must not set the AssociationLinkUrl to null since that would disable templating on it, but we want templating to work if the association link was not in the payload.
            if (associationLinkUrl != null && navigationLink.AssociationLinkUrl == null)
            {
                navigationLink.AssociationLinkUrl = associationLinkUrl;
            }
        }
        /// <summary>
        /// Validates the specified <paramref name="property"/> matches 
        /// the parsed <paramref name="link"/>.
        /// </summary>
        /// <param name="property">Property as understood by the type system.</param>
        /// <param name="link">Property as parsed.</param>
        /// <param name="model">Client Model.</param>
        /// <param name="performEntityCheck">whether to do the entity check or not.</param>
        /// <returns>The type</returns>
        internal static Type ValidatePropertyMatch(ClientPropertyAnnotation property, ODataNavigationLink link, ClientEdmModel model, bool performEntityCheck)
        {
            Debug.Assert(property != null, "property != null");
            Debug.Assert(link != null, "link != null");

            Type propertyType = null;
            if (link.IsCollection.HasValue)
            {
                if (link.IsCollection.Value)
                {
                    // We need to fail if the payload states that the property is a navigation collection property
                    // and in the client, the property is not a collection property.
                    if (!property.IsEntityCollection)
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkFeedPropertyNotCollection(property.PropertyName));
                    }

                    propertyType = property.EntityCollectionItemType;
                }
                else
                {
                    if (property.IsEntityCollection)
                    {
                        throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MismatchAtomLinkEntryPropertyIsCollection(property.PropertyName));
                    }

                    propertyType = property.PropertyType;
                }
            }

            // If the server type and the client type does not match, we need to throw.
            // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa
            if (propertyType != null && performEntityCheck)
            {
                if (!ClientTypeUtil.TypeIsEntity(propertyType, model))
                {
                    throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidNonEntityType(propertyType.ToString()));
                }
            }

            return propertyType;
        }
Beispiel #15
0
        /// <summary>
        /// Checks the <paramref name="navigationLink"/> for duplicate property names in an entry when the navigation link
        /// has started but we don't know yet if it's expanded or not.
        /// </summary>
        /// <param name="navigationLink">The navigation link to be checked.</param>
        internal void CheckForDuplicatePropertyNamesOnNavigationLinkStart(ODataNavigationLink navigationLink)
        {
            if (this.disabled)
            {
                return;
            }

            Debug.Assert(navigationLink != null, "navigationLink != null");
#if DEBUG
            this.startNavigationLinkName = navigationLink.Name;
#endif

            // Just check for duplication without modifying anything in the caches - this is to allow callers to choose whether they want to call this method first
            // or just call the CheckForDuplicatePropertyNames(ODataNavigationLink) directly.
            string            propertyName = navigationLink.Name;
            DuplicationRecord existingDuplicationRecord;
            if (this.propertyNameCache != null && this.propertyNameCache.TryGetValue(propertyName, out existingDuplicationRecord))
            {
                this.CheckNavigationLinkDuplicateNameForExistingDuplicationRecord(propertyName, existingDuplicationRecord);
            }
        }
Beispiel #16
0
        private static void WriteNavigationLinks(ODataWriter writer, object element, Uri parentEntryUri, IEdmEntityType parentEntityType, IEdmModel model, ODataVersion targetVersion, IEnumerable<string> expandedNavigationProperties) 
        {
            foreach (var navigationProperty in parentEntityType.NavigationProperties())
            {
                IEdmTypeReference propertyTypeReference = navigationProperty.Type;
                bool isCollection = navigationProperty.Type.IsCollection();

                var navigationLink = new ODataNavigationLink
                                         {
                                             Url = new Uri(parentEntryUri, navigationProperty.Name),
                                             IsCollection = isCollection,
                                             Name = navigationProperty.Name,
                                         };

                writer.WriteStart(navigationLink);

                if (expandedNavigationProperties.Contains(navigationProperty.Name))
                {
                    var propertyValue = DataContext.GetPropertyValue(element, navigationProperty.Name);

                    if (propertyValue != null)
                    {
                        var propertyEntityType = propertyTypeReference.Definition as IEdmEntityType;
                        IEdmEntitySet targetEntitySet = model.EntityContainer.EntitySets().Single(s => s.EntityType() == propertyEntityType);

                        if (isCollection)
                        {
                            WriteFeed(writer, propertyValue as IEnumerable, targetEntitySet, model, targetVersion, Enumerable.Empty<string>());
                        }
                        else
                        {
                            WriteEntry(writer, propertyValue, targetEntitySet, model, targetVersion, Enumerable.Empty<string>());
                        }
                    }
                }

                writer.WriteEnd();
            }
            
        }
Beispiel #17
0
        private void AddBoundNavigationPropertyAnnotation(ODataItem item, ODataNavigationLink navigationLink, object boundValue)
        {
            var annotation = item.GetAnnotation<BoundNavigationPropertyAnnotation>();
            if (annotation == null)
            { 
                annotation = new BoundNavigationPropertyAnnotation { BoundProperties = new List<Tuple<ODataNavigationLink, object>>() };
                item.SetAnnotation(annotation);
            }

            annotation.BoundProperties.Add(new Tuple<ODataNavigationLink, object>(navigationLink, boundValue));
        }
        internal static IEdmNavigationProperty ValidateNavigationLink(
            ODataNavigationLink navigationLink,
            IEdmEntityType declaringEntityType,
            ODataPayloadKind?expandedPayloadKind,
            bool bypassValidation = false)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(
                !expandedPayloadKind.HasValue ||
                expandedPayloadKind.Value == ODataPayloadKind.EntityReferenceLink ||
                expandedPayloadKind.Value == ODataPayloadKind.Entry ||
                expandedPayloadKind.Value == ODataPayloadKind.Feed,
                "If an expanded payload kind is specified it must be entry, feed or entity reference link.");

            if (bypassValidation)
            {
                return(declaringEntityType == null ? null : declaringEntityType.FindProperty(navigationLink.Name) as IEdmNavigationProperty);
            }

            // Navigation link must have a non-empty name
            if (string.IsNullOrEmpty(navigationLink.Name))
            {
                throw new ODataException(Strings.ValidationUtils_LinkMustSpecifyName);
            }

            // If we write an entity reference link, don't validate the multiplicity of the IsCollection
            // property if it is 'false' (since we allow writing a singleton navigation link for
            // a collection navigation property in requests) nor the consistency of payload kind and metadata
            // (which is done separately in ODataWriterCore.CheckForNavigationLinkWithContent).
            bool isEntityReferenceLinkPayload = expandedPayloadKind == ODataPayloadKind.EntityReferenceLink;

            // true only if the expandedPayloadKind has a value and the value is 'Feed'
            bool isFeedPayload = expandedPayloadKind == ODataPayloadKind.Feed;

            // Make sure the IsCollection property agrees with the payload kind for entry and feed payloads
            Func <object, string> errorTemplate = null;

            if (!isEntityReferenceLinkPayload && navigationLink.IsCollection.HasValue && expandedPayloadKind.HasValue)
            {
                // For feed/entry make sure the IsCollection property is set correctly.
                if (isFeedPayload != navigationLink.IsCollection.Value)
                {
                    errorTemplate = expandedPayloadKind.Value == ODataPayloadKind.Feed
                        ? (Func <object, string>)Strings.WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedContent
                        : Strings.WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryContent;
                }
            }

            IEdmNavigationProperty navigationProperty = null;

            if (errorTemplate == null && declaringEntityType != null)
            {
                navigationProperty = WriterValidationUtils.ValidateNavigationPropertyDefined(navigationLink.Name, declaringEntityType);
                Debug.Assert(navigationProperty != null, "If we have a declaring type we expect a non-null navigation property since open nav props are not allowed.");

                bool isCollectionType = navigationProperty.Type.TypeKind() == EdmTypeKind.Collection;

                // Make sure the IsCollection property agrees with the metadata type for entry and feed payloads
                if (navigationLink.IsCollection.HasValue && isCollectionType != navigationLink.IsCollection)
                {
                    // Ignore the case where IsCollection is 'false' and we are writing an entity reference link
                    // (see comment above)
                    if (!(navigationLink.IsCollection == false && isEntityReferenceLinkPayload))
                    {
                        errorTemplate = isCollectionType
                            ? (Func <object, string>)Strings.WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedMetadata
                            : Strings.WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryMetadata;
                    }
                }

                // Make sure that the payload kind agrees with the metadata.
                // For entity reference links we check separately in ODataWriterCore.CheckForNavigationLinkWithContent.
                if (!isEntityReferenceLinkPayload && expandedPayloadKind.HasValue && isCollectionType != isFeedPayload)
                {
                    errorTemplate = isCollectionType
                        ? (Func <object, string>)Strings.WriterValidationUtils_ExpandedLinkWithEntryPayloadAndFeedMetadata
                        : Strings.WriterValidationUtils_ExpandedLinkWithFeedPayloadAndEntryMetadata;
                }
            }

            if (errorTemplate != null)
            {
                string uri = navigationLink.Url == null ? "null" : UriUtils.UriToString(navigationLink.Url);
                throw new ODataException(errorTemplate(uri));
            }

            return(navigationProperty);
        }
Beispiel #19
0
 /// <summary>
 /// Start writing a navigation link.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 public abstract void WriteStart(ODataNavigationLink navigationLink);
Beispiel #20
0
        /// <summary>
        /// Verifies that calling WriteStart navigation link is valid.
        /// </summary>
        /// <param name="synchronousCall">true if the call is to be synchronous; false otherwise.</param>
        /// <param name="navigationLink">Navigation link to write.</param>
        private void VerifyCanWriteStartNavigationLink(bool synchronousCall, ODataNavigationLink navigationLink)
        {
            ExceptionUtils.CheckArgumentNotNull(navigationLink, "navigationLink");

            this.VerifyNotDisposed();
            this.VerifyCallAllowed(synchronousCall);
        }
Beispiel #21
0
        /// <summary>
        /// Start writing a navigation link - implementation of the actual functionality.
        /// </summary>
        /// <param name="navigationLink">Navigation link to write.</param>
        private void WriteStartNavigationLinkImplementation(ODataNavigationLink navigationLink)
        {
            this.EnterScope(WriterState.NavigationLink, navigationLink);

            // If the parent entry has a metadata builder, use that metadatabuilder on the navigation link as well.
            Debug.Assert(this.scopes.Parent != null, "Navigation link scopes must have a parent scope.");
            Debug.Assert(this.scopes.Parent.Item is ODataEntry, "The parent of a navigation link scope should always be an entry");
            ODataEntry parentEntry = (ODataEntry)this.scopes.Parent.Item;
            if (parentEntry.MetadataBuilder != null)
            {
                navigationLink.MetadataBuilder = parentEntry.MetadataBuilder;
            }
        }
Beispiel #22
0
 /// <summary>
 /// Write an entity reference link into a navigation link content.
 /// </summary>
 /// <param name="parentNavigationLink">The parent navigation link which is being written around the entity reference link.</param>
 /// <param name="entityReferenceLink">The entity reference link to write.</param>
 protected abstract void WriteEntityReferenceInNavigationLinkContent(ODataNavigationLink parentNavigationLink, ODataEntityReferenceLink entityReferenceLink);
Beispiel #23
0
 /// <summary>
 /// Start writing a navigation link.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 public abstract void WriteStart(ODataNavigationLink navigationLink);
Beispiel #24
0
 /// <summary>
 /// Validates that the specified navigation link has a Url.
 /// </summary>
 /// <param name="navigationLink">The navigation link to validate.</param>
 public void ValidateNavigationLinkUrlPresent(ODataNavigationLink navigationLink)
 {
 }
Beispiel #25
0
 /// <summary>
 /// Validates that the sepcified navigation link has cardinality, that is it has the IsCollection value set.
 /// </summary>
 /// <param name="navigationLink">The navigation link to validate.</param>
 public void ValidateNavigationLinkHasCardinality(ODataNavigationLink navigationLink)
 {
     WriterValidationUtils.ValidateNavigationLinkHasCardinality(navigationLink);
 }
        public void WriteStartOnExpandedFeedWithDeltaLinkShouldIgnoreDeltaLink()
        {
            string expectedPayload = 
                @"<?xml version=""1.0"" encoding=""utf-8""?>"
                + @"<entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:d=""http://docs.oasis-open.org/odata/ns/data"" xmlns:m=""http://docs.oasis-open.org/odata/ns/metadata"" xmlns:georss=""http://www.georss.org/georss"" xmlns:gml=""http://www.opengis.net/gml"" m:context=""http://www.example.com/$metadata#TestEntitySet/$entity"">"
                + @"<link rel=""http://docs.oasis-open.org/odata/ns/related/ResourceSetNavigationProperty"" type=""application/atom+xml;type=feed"" title=""ResourceSetNavigationProperty"" href=""http://host/navProp"">"
                    +"<m:inline><feed><id>http://host/TestEntitySet</id><title />";

            Action<ODataWriter> deltaLinkAtWriteStart = (odataWriter) =>
            {
                var entryToWrite = new ODataEntry { Properties = new[] { new ODataProperty { Name = "ID", Value = 1 } } };
                odataWriter.WriteStart(entryToWrite);

                ODataNavigationLink navLink = new ODataNavigationLink { Name = "ResourceSetNavigationProperty", IsCollection = true, Url = new Uri("http://host/navProp")  };
                odataWriter.WriteStart(navLink);

                var feedToWrite = new ODataFeed() { Id = new Uri("http://host/TestEntitySet", UriKind.Absolute) };
                feedToWrite.DeltaLink = new Uri("http://host/relative", UriKind.Absolute);
                odataWriter.WriteStart(feedToWrite);
            };

            WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Atom, expectedPayload, request: true, createFeedWriter: false);
            WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Atom, expectedPayload, request: false, createFeedWriter: false);
        }
        /// <summary>
        /// Validates that the sepcified navigation link has cardinality, that is it has the IsCollection value set.
        /// </summary>
        /// <param name="navigationLink">The navigation link to validate.</param>
        internal static void ValidateNavigationLinkHasCardinality(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            if (!navigationLink.IsCollection.HasValue)
            {
                throw new ODataException(Strings.WriterValidationUtils_NavigationLinkMustSpecifyIsCollection(navigationLink.Name));
            }
        }
        internal static IEdmNavigationProperty ValidateNavigationLink(
            ODataNavigationLink navigationLink,
            IEdmEntityType declaringEntityType,
            ODataPayloadKind? expandedPayloadKind,
            bool bypassValidation = false)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(
                !expandedPayloadKind.HasValue ||
                expandedPayloadKind.Value == ODataPayloadKind.EntityReferenceLink ||
                expandedPayloadKind.Value == ODataPayloadKind.Entry ||
                expandedPayloadKind.Value == ODataPayloadKind.Feed,
                "If an expanded payload kind is specified it must be entry, feed or entity reference link.");

            if (bypassValidation)
            {
                return declaringEntityType == null ? null : declaringEntityType.FindProperty(navigationLink.Name) as IEdmNavigationProperty;
            }

            // Navigation link must have a non-empty name
            if (string.IsNullOrEmpty(navigationLink.Name))
            {
                throw new ODataException(Strings.ValidationUtils_LinkMustSpecifyName);
            }

            // If we write an entity reference link, don't validate the multiplicity of the IsCollection
            // property if it is 'false' (since we allow writing a singleton navigation link for
            // a collection navigation property in requests) nor the consistency of payload kind and metadata
            // (which is done separately in ODataWriterCore.CheckForNavigationLinkWithContent).
            bool isEntityReferenceLinkPayload = expandedPayloadKind == ODataPayloadKind.EntityReferenceLink;

            // true only if the expandedPayloadKind has a value and the value is 'Feed'
            bool isFeedPayload = expandedPayloadKind == ODataPayloadKind.Feed;

            // Make sure the IsCollection property agrees with the payload kind for entry and feed payloads
            Func<object, string> errorTemplate = null;
            if (!isEntityReferenceLinkPayload && navigationLink.IsCollection.HasValue && expandedPayloadKind.HasValue)
            {
                // For feed/entry make sure the IsCollection property is set correctly.
                if (isFeedPayload != navigationLink.IsCollection.Value)
                {
                    errorTemplate = expandedPayloadKind.Value == ODataPayloadKind.Feed
                        ? (Func<object, string>)Strings.WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedContent
                        : Strings.WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryContent;
                }
            }

            IEdmNavigationProperty navigationProperty = null;
            if (errorTemplate == null && declaringEntityType != null)
            {
                navigationProperty = WriterValidationUtils.ValidateNavigationPropertyDefined(navigationLink.Name, declaringEntityType);
                Debug.Assert(navigationProperty != null, "If we have a declaring type we expect a non-null navigation property since open nav props are not allowed.");

                bool isCollectionType = navigationProperty.Type.TypeKind() == EdmTypeKind.Collection;

                // Make sure the IsCollection property agrees with the metadata type for entry and feed payloads
                if (navigationLink.IsCollection.HasValue && isCollectionType != navigationLink.IsCollection)
                {
                    // Ignore the case where IsCollection is 'false' and we are writing an entity reference link
                    // (see comment above)
                    if (!(navigationLink.IsCollection == false && isEntityReferenceLinkPayload))
                    {
                        errorTemplate = isCollectionType
                            ? (Func<object, string>)Strings.WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedMetadata
                            : Strings.WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryMetadata;
                    }
                }

                // Make sure that the payload kind agrees with the metadata.
                // For entity reference links we check separately in ODataWriterCore.CheckForNavigationLinkWithContent.
                if (!isEntityReferenceLinkPayload && expandedPayloadKind.HasValue && isCollectionType != isFeedPayload)
                {
                    errorTemplate = isCollectionType
                        ? (Func<object, string>)Strings.WriterValidationUtils_ExpandedLinkWithEntryPayloadAndFeedMetadata
                        : Strings.WriterValidationUtils_ExpandedLinkWithFeedPayloadAndEntryMetadata;
                }
            }

            if (errorTemplate != null)
            {
                string uri = navigationLink.Url == null ? "null" : UriUtils.UriToString(navigationLink.Url);
                throw new ODataException(errorTemplate(uri));
            }

            return navigationProperty;
        }
 /// <summary>
 /// Visits a navigation link item.
 /// </summary>
 /// <param name="navigationLink">The navigation link to visit.</param>
 protected override void VisitNavigationLink(ODataNavigationLink navigationLink)
 {
     this.ValidateUri(navigationLink.Url);
     base.VisitNavigationLink(navigationLink);
 }
Beispiel #30
0
 /// <summary>
 /// Creates a new navigation link scope.
 /// </summary>
 /// <param name="writerState">The writer state for the new scope.</param>
 /// <param name="navLink">The navigation link for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 /// <returns>The newly created navigation link scope.</returns>
 protected virtual NavigationLinkScope CreateNavigationLinkScope(WriterState writerState, ODataNavigationLink navLink, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
 {
     return new NavigationLinkScope(writerState, navLink, navigationSource, entityType, skipWriting, selectedProperties, odataUri);
 }
Beispiel #31
0
        /// <summary>
        /// Writes a navigation link.
        /// </summary>
        /// <param name="entityDescriptor">The entity</param>
        /// <param name="relatedLinks">The links related to the entity</param>
        /// <param name="odataWriter">The ODataWriter used to write the navigation link.</param>
        internal void WriteNavigationLink(EntityDescriptor entityDescriptor, IEnumerable<LinkDescriptor> relatedLinks, ODataWriterWrapper odataWriter)
        {
            // TODO: create instance of odatawriter.
            // TODO: send clientType once, so that we dont need entity descriptor
            Debug.Assert(EntityStates.Added == entityDescriptor.State, "entity not added state");

            Dictionary<string, List<LinkDescriptor>> groupRelatedLinks = new Dictionary<string, List<LinkDescriptor>>(EqualityComparer<string>.Default);
            foreach (LinkDescriptor end in relatedLinks)
            {
                List<LinkDescriptor> linkDescriptorsList = null;
                if (!groupRelatedLinks.TryGetValue(end.SourceProperty, out linkDescriptorsList))
                {
                    linkDescriptorsList = new List<LinkDescriptor>();
                    groupRelatedLinks.Add(end.SourceProperty, linkDescriptorsList);
                }

                linkDescriptorsList.Add(end);
            }

            ClientTypeAnnotation clientType = null;
            foreach (var grlinks in groupRelatedLinks)
            {
                if (null == clientType)
                {
                    ClientEdmModel model = this.requestInfo.Model;
                    clientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                }

                bool isCollection = clientType.GetProperty(grlinks.Key, false).IsEntityCollection;
                bool started = false;

                foreach (LinkDescriptor end in grlinks.Value)
                {
                    Debug.Assert(!end.ContentGeneratedForSave, "already saved link");
                    end.ContentGeneratedForSave = true;
                    Debug.Assert(null != end.Target, "null is DELETE");

                    ODataNavigationLink navigationLink = new ODataNavigationLink();
                    navigationLink.Url = this.requestInfo.EntityTracker.GetEntityDescriptor(end.Target).GetLatestEditLink();
                    Debug.Assert(Uri.IsWellFormedUriString(UriUtil.UriToString(navigationLink.Url), UriKind.Absolute), "Uri.IsWellFormedUriString(targetEditLink, UriKind.Absolute)");

                    navigationLink.IsCollection = isCollection;
                    navigationLink.Name = grlinks.Key;

                    if (!started)
                    {
                        odataWriter.WriteNavigationLinksStart(navigationLink);
                        started = true;
                    }

                    odataWriter.WriteNavigationLinkStart(navigationLink, end.Source, end.Target);
                    odataWriter.WriteEntityReferenceLink(new ODataEntityReferenceLink() { Url = navigationLink.Url }, end.Source, end.Target);
                    odataWriter.WriteNavigationLinkEnd(navigationLink, end.Source, end.Target);
                }

                odataWriter.WriteNavigationLinksEnd();
            }
        }
Beispiel #32
0
 /// <summary>
 /// Asynchronously start writing a navigation link.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 /// <returns>A task instance that represents the asynchronous write operation.</returns>
 public abstract Task WriteStartAsync(ODataNavigationLink navigationLink);
Beispiel #33
0
 /// <summary>
 /// Asynchronously start writing a navigation link.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 /// <returns>A task instance that represents the asynchronous write operation.</returns>
 public abstract Task WriteStartAsync(ODataNavigationLink navigationLink);
Beispiel #34
0
        /// <summary>
        /// Check the <paramref name="navigationLink"/> for duplicate property names in an entry or complex value.
        /// If not explicitly allowed throw when duplicate properties are detected.
        /// If duplicate properties are allowed see the comment on ODataWriterBehavior.AllowDuplicatePropertyNames
        /// or ODataReaderBehavior.AllowDuplicatePropertyNames for further details.
        /// </summary>
        /// <param name="navigationLink">The navigation link to be checked.</param>
        /// <param name="isExpanded">true if the link is expanded, false otherwise.</param>
        /// <param name="isCollection">true if the navigation link is a collection, false if it's a singleton or null if we don't know.</param>
        /// <returns>The association link uri with the same name if there already was one.</returns>
        internal Uri CheckForDuplicatePropertyNames(ODataNavigationLink navigationLink, bool isExpanded, bool?isCollection)
        {
            if (this.disabled)
            {
                return(null);
            }
#if DEBUG
            this.startNavigationLinkName = null;
#endif

            string            propertyName = navigationLink.Name;
            DuplicationRecord existingDuplicationRecord;
            if (!this.TryGetDuplicationRecord(propertyName, out existingDuplicationRecord))
            {
                DuplicationRecord duplicationRecord = new DuplicationRecord(DuplicationKind.NavigationProperty);
                ApplyNavigationLinkToDuplicationRecord(duplicationRecord, navigationLink, isExpanded, isCollection);
                this.propertyNameCache.Add(propertyName, duplicationRecord);
                return(null);
            }
            else
            {
                // First check for duplication without expansion knowledge.
                this.CheckNavigationLinkDuplicateNameForExistingDuplicationRecord(propertyName, existingDuplicationRecord);

                if (existingDuplicationRecord.DuplicationKind == DuplicationKind.PropertyAnnotationSeen ||
                    (existingDuplicationRecord.DuplicationKind == DuplicationKind.NavigationProperty &&
                     existingDuplicationRecord.AssociationLinkName != null &&
                     existingDuplicationRecord.NavigationLink == null))
                {
                    // If the existing one is just an association link, update it to include the navigation link portion as well
                    ApplyNavigationLinkToDuplicationRecord(existingDuplicationRecord, navigationLink, isExpanded, isCollection);
                }
                else if (this.allowDuplicateProperties)
                {
                    Debug.Assert(
                        (existingDuplicationRecord.DuplicationKind == DuplicationKind.PotentiallyAllowed || existingDuplicationRecord.DuplicationKind == DuplicationKind.NavigationProperty),
                        "We should have already taken care of prohibit duplication.");

                    // If the configuration explicitly allows duplication, then just turn the existing property into a nav link with all the information we have
                    existingDuplicationRecord.DuplicationKind = DuplicationKind.NavigationProperty;
                    ApplyNavigationLinkToDuplicationRecord(existingDuplicationRecord, navigationLink, isExpanded, isCollection);
                }
                else
                {
                    // We've found two navigation links in a request
                    Debug.Assert(
                        existingDuplicationRecord.DuplicationKind == DuplicationKind.NavigationProperty && existingDuplicationRecord.NavigationLink != null && !this.isResponse,
                        "We can only get here if we've found two navigation links in a request.");

                    bool?isCollectionEffectiveValue = GetIsCollectionEffectiveValue(isExpanded, isCollection);

                    // If one of them is a definitive singleton, then we fail.
                    if (isCollectionEffectiveValue == false || existingDuplicationRecord.NavigationPropertyIsCollection == false)
                    {
                        // This is the case where an expanded singleton is followed by a deferred link for example.
                        // Once we know for sure that the nav. prop. is a singleton we can't allow more than one link for it.
                        throw new ODataException(Strings.DuplicatePropertyNamesChecker_MultipleLinksForSingleton(propertyName));
                    }

                    // Otherwise allow it, but update the link with the new information
                    if (isCollectionEffectiveValue.HasValue)
                    {
                        existingDuplicationRecord.NavigationPropertyIsCollection = isCollectionEffectiveValue;
                    }
                }

                return(existingDuplicationRecord.AssociationLinkUrl);
            }
        }
        /// <summary>
        /// Validates that the specified navigation link has a Url.
        /// </summary>
        /// <param name="navigationLink">The navigation link to validate.</param>
        internal static void ValidateNavigationLinkUrlPresent(ODataNavigationLink navigationLink)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            // Navigation link must specify the Url
            // NOTE: we currently only require a non-null Url for ATOM payloads and non-expanded navigation links in JSON.
            //       There is no place in JSON to write a Url if the navigation link is expanded.
            if (navigationLink.Url == null)
            {
                throw new ODataException(Strings.WriterValidationUtils_NavigationLinkMustSpecifyUrl(navigationLink.Name));
            }
        }
Beispiel #36
0
        /// <summary>
        /// Sets the properties on a duplication record for a navigation link.
        /// </summary>
        /// <param name="duplicationRecord">The duplication record to modify.</param>
        /// <param name="navigationLink">The navigation link found for this property.</param>
        /// <param name="isExpanded">true if the navigation link is expanded, false otherwise.</param>
        /// <param name="isCollection">true if the navigation link is marked as collection, false if it's marked as singletong or null if we don't know.</param>
        private static void ApplyNavigationLinkToDuplicationRecord(DuplicationRecord duplicationRecord, ODataNavigationLink navigationLink, bool isExpanded, bool?isCollection)
        {
            duplicationRecord.DuplicationKind = DuplicationKind.NavigationProperty;
            duplicationRecord.NavigationLink  = navigationLink;

            // We only take the cardinality of the link for granted if it was expanded or if it is a collection.
            // We can't rely on singleton deferred links to know the cardinality since they can be used for binding even if the actual link is a collection.
            duplicationRecord.NavigationPropertyIsCollection = GetIsCollectionEffectiveValue(isExpanded, isCollection);
        }
        /// <summary>
        /// Creates the <see cref="ODataNavigationLink"/> to be written while writing this entity.
        /// </summary>
        /// <param name="navigationProperty">The navigation property for which the navigation link is being created.</param>
        /// <param name="entityInstanceContext">The context for the entity instance being written.</param>
        /// <returns>The navigation link to be written.</returns>
        public virtual ODataNavigationLink CreateNavigationLink(IEdmNavigationProperty navigationProperty, EntityInstanceContext entityInstanceContext)
        {
            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigationProperty");
            }
            if (entityInstanceContext == null)
            {
                throw Error.ArgumentNull("entityInstanceContext");
            }

            ODataSerializerContext writeContext = entityInstanceContext.SerializerContext;
            ODataNavigationLink navigationLink = null;

            if (writeContext.NavigationSource != null)
            {
                IEdmTypeReference propertyType = navigationProperty.Type;
                IEdmModel model = writeContext.Model;
                NavigationSourceLinkBuilderAnnotation linkBuilder = model.GetNavigationSourceLinkBuilder(writeContext.NavigationSource);
                Uri navigationUrl = linkBuilder.BuildNavigationLink(entityInstanceContext, navigationProperty, writeContext.MetadataLevel);

                navigationLink = new ODataNavigationLink
                {
                    IsCollection = propertyType.IsCollection(),
                    Name = navigationProperty.Name,
                };

                if (navigationUrl != null)
                {
                    navigationLink.Url = navigationUrl;
                }
            }

            return navigationLink;
        }
Beispiel #38
0
 /// <summary>
 /// Write a deferred (non-expanded) navigation link.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 protected abstract void WriteDeferredNavigationLink(ODataNavigationLink navigationLink);
        public void WriteEndOnExpandedFeedWithDeltaLinkShouldThrow()
        {
            Action<ODataWriter> deltaLinkAtWriteStart = (odataWriter) =>
            {
                var entryToWrite = new ODataEntry { Properties = new[] { new ODataProperty { Name = "ID", Value = 1 } } };
                odataWriter.WriteStart(entryToWrite);

                ODataNavigationLink navLink = new ODataNavigationLink { Name = "ResourceSetNavigationProperty", IsCollection = true, Url = new Uri("http://host/navProp") };
                odataWriter.WriteStart(navLink);

                var feedToWrite = new ODataFeed() { Id = new Uri("http://host/TestEntitySet", UriKind.Absolute) };
                feedToWrite.DeltaLink = new Uri("http://host/relative", UriKind.Absolute);
                odataWriter.WriteStart(feedToWrite);
                odataWriter.WriteEnd();
            };

            Action requestTest = () => WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Atom, string.Empty, request: true, createFeedWriter: false);
            requestTest.ShouldThrow<ODataException>().WithMessage(Strings.ODataWriterCore_DeltaLinkNotSupportedOnExpandedFeed);

            Action responseTest = () => WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Atom, string.Empty, request: false, createFeedWriter: false);
            responseTest.ShouldThrow<ODataException>().WithMessage(Strings.ODataWriterCore_DeltaLinkNotSupportedOnExpandedFeed);
        }
Beispiel #40
0
 /// <summary>
 /// Finish writing a navigation link with content.
 /// </summary>
 /// <param name="navigationLink">The navigation link to write.</param>
 protected abstract void EndNavigationLinkWithContent(ODataNavigationLink navigationLink);
Beispiel #41
0
 /// <summary>
 /// Validates that the sepcified navigation link has cardinality, that is it has the IsCollection value set.
 /// </summary>
 /// <param name="navigationLink">The navigation link to validate.</param>
 public void ValidateNavigationLinkHasCardinality(ODataNavigationLink navigationLink)
 {
 }
Beispiel #42
0
 /// <summary>
 /// Validates that the specified navigation link has a Url.
 /// </summary>
 /// <param name="navigationLink">The navigation link to validate.</param>
 public void ValidateNavigationLinkUrlPresent(ODataNavigationLink navigationLink)
 {
     WriterValidationUtils.ValidateNavigationLinkUrlPresent(navigationLink);
 }