internal static Uri GenerateNavigationPropertyLink(EntityInstanceContext entityContext, IEdmNavigationProperty navigationProperty, EntitySetConfiguration configuration, bool includeCast)
        {
            string routeName;

            Dictionary<string, object> routeValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            routeValues.Add(LinkGenerationConstants.Controller, configuration.Name);
            routeValues.Add(LinkGenerationConstants.ParentId, ConventionsHelpers.GetEntityKeyValue(entityContext, configuration.EntityType));
            routeValues.Add(LinkGenerationConstants.NavigationProperty, navigationProperty.Name);

            if (includeCast)
            {
                routeName = ODataRouteNames.PropertyNavigationWithCast;
                routeValues.Add(LinkGenerationConstants.Entitytype, entityContext.EntityType.FullName());
            }
            else
            {
                routeName = ODataRouteNames.PropertyNavigation;
            }

            string link = entityContext.UrlHelper.Link(routeName, routeValues);

            if (link == null)
            {
                throw Error.InvalidOperation(SRResources.NavigationPropertyRouteMissingOrIncorrect, navigationProperty.Name, ODataRouteNames.PropertyNavigation);
            }

            return new Uri(link);
        }
 /// <summary>
 /// Build a segment representing a navigation property.
 /// </summary>
 /// <param name="path">Path to perform the computation on.</param>
 /// <param name="navigationProperty">The navigation property this segment represents.</param>
 /// <param name="navigationSource">The navigation source of the entities targetted by this navigation property. This can be null.</param>
 /// <returns>The ODataPath with navigation property appended in the end in the end</returns>
 public static ODataPath AppendNavigationPropertySegment(this ODataPath path, IEdmNavigationProperty navigationProperty, IEdmNavigationSource navigationSource)
 {
     var newPath = new ODataPath(path);
     NavigationPropertySegment np = new NavigationPropertySegment(navigationProperty, navigationSource);
     newPath.Add(np);
     return newPath;
 }
        /// <summary>
        /// Finds the navigation source that a navigation property targets.
        /// </summary>
        /// <param name="property">The navigation property.</param>
        /// <returns>The navigation source that the navigation propertion targets, or null if no such navigation source exists.</returns>
        public virtual IEdmNavigationSource FindNavigationTarget(IEdmNavigationProperty property)
        {
            EdmUtil.CheckArgumentNull(property, "property");

            if (!property.ContainsTarget)
            {
                IEdmNavigationSource result;
                if (this.navigationPropertyMappings.TryGetValue(property, out result))
                {
                    return result;
                }
            }
            else
            {
                return EdmUtil.DictionaryGetOrUpdate(
                    this.containedNavigationPropertyCache,
                    property,
                    navProperty => new EdmContainedEntitySet(this, navProperty));
            }

            return EdmUtil.DictionaryGetOrUpdate(
                this.unknownNavigationPropertyCache,
                property,
                navProperty => new EdmUnknownEntitySet(this, navProperty));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ODataSerializerContext"/> class.
        /// </summary>
        /// <param name="entity">The entity whose navigation property is being expanded.</param>
        /// <param name="selectExpandClause">The <see cref="SelectExpandClause"/> for the navigation property being expanded.</param>
        /// <param name="navigationProperty">The navigation property being expanded.</param>
        /// <remarks>This constructor is used to construct the serializer context for writing expanded properties.</remarks>
        public ODataSerializerContext(EntityInstanceContext entity, SelectExpandClause selectExpandClause, IEdmNavigationProperty navigationProperty)
        {
            if (entity == null)
            {
                throw Error.ArgumentNull("entity");
            }
            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigationProperty");
            }

            ODataSerializerContext context = entity.SerializerContext;

            Request = context.Request;
            Url = context.Url;
            EntitySet = context.EntitySet;
            Model = context.Model;
            Path = context.Path;
            RootElementName = context.RootElementName;
            SkipExpensiveAvailabilityChecks = context.SkipExpensiveAvailabilityChecks;
            MetadataLevel = context.MetadataLevel;

            ExpandedEntity = entity;
            SelectExpandClause = selectExpandClause;
            NavigationProperty = navigationProperty;
            EntitySet = context.EntitySet.FindNavigationTarget(navigationProperty);
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link.</param>
        /// <param name="navigationProperty">The navigation property for the link, if it's available.</param>
        internal ODataAtomReaderNavigationLinkDescriptor(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
        }
        /// <summary>
        /// Generates a navigation link following the OData URL conventions for the entity represented by <paramref name="entityContext"/> and the given 
        /// navigation property.
        /// </summary>
        /// <param name="entityContext">The <see cref="EntityInstanceContext"/> representing the entity for which the navigation link needs to be generated.</param>
        /// <param name="navigationProperty">The EDM navigation property.</param>
        /// <param name="includeCast">Represents whether the generated link should have a cast segment representing a type cast.</param>
        /// <returns>The navigation link following the OData URL conventions.</returns>
        public static Uri GenerateNavigationPropertyLink(this EntityInstanceContext entityContext, IEdmNavigationProperty navigationProperty, bool includeCast)
        {
            if (entityContext == null)
            {
                throw Error.ArgumentNull("entityContext");
            }
            if (entityContext.Url == null)
            {
                throw Error.Argument("entityContext", SRResources.UrlHelperNull, typeof(EntityInstanceContext).Name);
            }

            List<ODataPathSegment> navigationPathSegments = new List<ODataPathSegment>();
            navigationPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
            navigationPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext)));

            if (includeCast)
            {
                navigationPathSegments.Add(new CastPathSegment(entityContext.EntityType));
            }

            navigationPathSegments.Add(new NavigationPathSegment(navigationProperty));

            string link = entityContext.Url.CreateODataLink(navigationPathSegments);
            if (link == null)
            {
                return null;
            }

            return new Uri(link);
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link.</param>
        /// <param name="navigationProperty">The navigation property for the link, if it's available.</param>
        internal ODataAtomReaderNavigationLinkDescriptor(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(navigationLink != null, "navigationLink != null");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
        }
		internal NavigationMetadata(IEdmNavigationProperty edmNavigationProperty, IEntitySetMetadata targetEntitySet)
		{
			Contract.Assert(edmNavigationProperty != null);
			Contract.Assert(targetEntitySet != null);

			EdmNavigationProperty = edmNavigationProperty;
			TargetEntitySet = targetEntitySet;
		}
        public override Uri BuildNavigationLink(EntityInstanceContext context, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel)
        {
            if (NavigationLinkBuilder != null)
            {
                return NavigationLinkBuilder(context, navigationProperty, metadataLevel);
            }

            return null;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="EdmUnknownEntitySet"/> class.
        /// </summary>
        /// <param name="parentNavigationSource">The <see cref="IEdmNavigationSource"/> that container element belongs to</param>
        /// <param name="navigationProperty">An <see cref="IEdmNavigationProperty"/> containing the navagation property definition of the contained element</param>
        public EdmUnknownEntitySet(IEdmNavigationSource parentNavigationSource, IEdmNavigationProperty navigationProperty)
            : base(navigationProperty.Name, navigationProperty.ToEntityType())
        {
            EdmUtil.CheckArgumentNull(parentNavigationSource, "parentNavigationSource");
            EdmUtil.CheckArgumentNull(navigationProperty, "navigationProperty");

            this.parentNavigationSource = parentNavigationSource;
            this.navigationProperty = navigationProperty;
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="navigationLink">The navigation link to report.</param>
        /// <param name="navigationProperty">The navigation property for which the link will be reported.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        private ODataJsonLightReaderNavigationLinkInfo(ODataNavigationLink navigationLink, IEdmNavigationProperty navigationProperty, bool isExpanded)
        {
            Debug.Assert(navigationLink != null, "navigationLink != null");
            Debug.Assert(navigationProperty == null || navigationProperty.Name == navigationLink.Name, "The name of the navigation link doesn't match the name of the property.");

            this.navigationLink = navigationLink;
            this.navigationProperty = navigationProperty;
            this.isExpanded = isExpanded;
        }
		public static void GetAssociationAnnotations(this IEdmModel model, IEdmNavigationProperty property, out IEnumerable<IEdmDirectValueAnnotation> annotations, out IEnumerable<IEdmDirectValueAnnotation> end1Annotations, out IEnumerable<IEdmDirectValueAnnotation> end2Annotations, out IEnumerable<IEdmDirectValueAnnotation> constraintAnnotations)
		{
			annotations = null;
			end1Annotations = null;
			end2Annotations = null;
			constraintAnnotations = null;
			EdmUtil.CheckArgumentNull<IEdmModel>(model, "model");
			EdmUtil.CheckArgumentNull<IEdmNavigationProperty>(property, "property");
			property.PopulateCaches();
			SerializationExtensionMethods.AssociationAnnotations annotationValue = model.GetAnnotationValue<SerializationExtensionMethods.AssociationAnnotations>(property, "http://schemas.microsoft.com/ado/2011/04/edm/internal", "AssociationAnnotations");
			if (annotationValue == null)
			{
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations = Enumerable.Empty<IEdmDirectValueAnnotation>();
				annotations = edmDirectValueAnnotations;
				end1Annotations = edmDirectValueAnnotations;
				end2Annotations = edmDirectValueAnnotations;
				constraintAnnotations = edmDirectValueAnnotations;
				return;
			}
			else
			{
				IEnumerable<IEdmDirectValueAnnotation> enumerablePointers = annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations1 = annotationValue.Annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations2 = edmDirectValueAnnotations1;
				if (edmDirectValueAnnotations1 == null)
				{
					edmDirectValueAnnotations2 = Enumerable.Empty<IEdmDirectValueAnnotation>();
				}
				(enumerablePointers) = edmDirectValueAnnotations2;
				IEnumerable<IEdmDirectValueAnnotation> enumerablePointers1 = end1Annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations3 = annotationValue.End1Annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations4 = edmDirectValueAnnotations3;
				if (edmDirectValueAnnotations3 == null)
				{
					edmDirectValueAnnotations4 = Enumerable.Empty<IEdmDirectValueAnnotation>();
				}
				(enumerablePointers1) = edmDirectValueAnnotations4;
				IEnumerable<IEdmDirectValueAnnotation> enumerablePointers2 = end2Annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations5 = annotationValue.End2Annotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations6 = edmDirectValueAnnotations5;
				if (edmDirectValueAnnotations5 == null)
				{
					edmDirectValueAnnotations6 = Enumerable.Empty<IEdmDirectValueAnnotation>();
				}
				(enumerablePointers2) = edmDirectValueAnnotations6;
				IEnumerable<IEdmDirectValueAnnotation> enumerablePointers3 = constraintAnnotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations7 = annotationValue.ConstraintAnnotations;
				IEnumerable<IEdmDirectValueAnnotation> edmDirectValueAnnotations8 = edmDirectValueAnnotations7;
				if (edmDirectValueAnnotations7 == null)
				{
					edmDirectValueAnnotations8 = Enumerable.Empty<IEdmDirectValueAnnotation>();
				}
				(enumerablePointers3) = edmDirectValueAnnotations8;
				return;
			}
		}
Beispiel #13
0
        /// <summary>
        /// Sets the navigation target for a particular navigation property.
        /// </summary>
        /// <param name="navigationProperty">The navigation property.</param>
        /// <param name="target">The target entity set.</param>
        public void SetNavigationTarget(IEdmNavigationProperty navigationProperty, IEdmEntitySet target)
        {
            this.navigationTargets[navigationProperty] = target;

            var stubTarget = (StubEdmEntitySet)target;
            if (stubTarget.FindNavigationTarget(navigationProperty.Partner) != this) 
            {
                stubTarget.SetNavigationTarget(navigationProperty.Partner, this);
            }
        }
Beispiel #14
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NavigationPathSegment" /> class.
        /// </summary>
        /// <param name="navigationProperty">The navigation property being accessed by this segment.</param>
        public NavigationPathSegment(IEdmNavigationProperty navigationProperty)
        {
            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigation");
            }

            NavigationProperty = navigationProperty;
            NavigationPropertyName = navigationProperty.Name;
        }
        /// <summary>
        /// Finds the entity set that a navigation property targets.
        /// </summary>
        /// <param name="property">The navigation property.</param>
        /// <returns>The entity set that the navigation property targets</returns>
        public override IEdmNavigationSource FindNavigationTarget(IEdmNavigationProperty property)
        {
            IEdmNavigationSource navigationTarget = base.FindNavigationTarget(property);
            if (navigationTarget is IEdmUnknownEntitySet)
            {
                return this.parentNavigationSource.FindNavigationTarget(property);
            }

            return navigationTarget;
        }
        /// <summary>
        /// Finds the entity set that a navigation property targets.
        /// </summary>
        /// <param name="navigationProperty">The navigation property.</param>
        /// <returns>The entity set that the navigation propertion targets, or null if no such entity set exists.</returns>
        public IEdmEntitySet FindNavigationTarget(IEdmNavigationProperty navigationProperty)
        {
            Util.CheckArgumentNull(navigationProperty, "navigationProperty");

            var navigationFacade = navigationProperty as EdmNavigationPropertyFacade;
            if (navigationFacade == null)
            {
                return null;
            }

            return navigationFacade.FindNavigationTarget(this.serverEntitySet);
        }
Beispiel #17
0
		public IEdmEntitySet FindNavigationTarget(IEdmNavigationProperty property)
		{
			IEdmEntitySet edmEntitySet = null;
			IEdmNavigationProperty edmNavigationProperty = property;
			if (edmNavigationProperty == null || !this.navigationPropertyMappings.TryGetValue(edmNavigationProperty, out edmEntitySet))
			{
				return null;
			}
			else
			{
				return edmEntitySet;
			}
		}
        /// <summary>
        /// Initializes a new instance of the <see cref="EdmNavigationPropertyFacade"/> class.
        /// </summary>
        /// <param name="name">The name of the property.</param>
        /// <param name="modelFacade">The model facade.</param>
        /// <param name="declaringTypeFacade">The type facade.</param>
        /// <param name="serverProperty">The server property if one exists.</param>
        /// <param name="clientProperty">The client property.</param>
        internal EdmNavigationPropertyFacade(string name, EdmModelFacade modelFacade, EdmEntityTypeFacade declaringTypeFacade, IEdmNavigationProperty serverProperty, IEdmNavigationProperty clientProperty)
        {
            Debug.Assert(clientProperty != null, "clientProperty != null");
            Debug.Assert(serverProperty != null, "serverProperty != null");
            Debug.Assert(modelFacade != null, "modelFacade != null");
            Debug.Assert(declaringTypeFacade != null, "declaringTypeFacade != null");

            this.Name = name;
            this.modelFacade = modelFacade;
            this.declaringTypeFacade = declaringTypeFacade;
            this.serverProperty = serverProperty;
            this.clientProperty = clientProperty;
        }
		public static string GetAssociationEndName(this IEdmModel model, IEdmNavigationProperty property)
		{
			EdmUtil.CheckArgumentNull<IEdmModel>(model, "model");
			EdmUtil.CheckArgumentNull<IEdmNavigationProperty>(property, "property");
			property.PopulateCaches();
			string annotationValue = model.GetAnnotationValue<string>(property, "http://schemas.microsoft.com/ado/2011/04/edm/internal", "AssociationEndName");
			string name = annotationValue;
			if (annotationValue == null)
			{
				name = property.Partner.Name;
			}
			return name;
		}
        private static string FindLinksActionName(ILookup<string, HttpActionDescriptor> actionMap,
            IEdmNavigationProperty navigationProperty, IEdmEntityType declaringType, HttpMethod method)
        {
            string actionNamePrefix;
            if (method == HttpMethod.Delete)
            {
                actionNamePrefix = DeleteLinkActionNamePrefix;
            }
            else
            {
                actionNamePrefix = CreateLinkActionNamePrefix;
            }

            return actionMap.FindMatchingAction(
                        actionNamePrefix + "To" + navigationProperty.Name + "From" + declaringType.Name,
                        actionNamePrefix + "To" + navigationProperty.Name,
                        actionNamePrefix);
        }
 /// <summary>
 /// Creates a new Silent partner for a navigation property
 /// </summary>
 /// <param name="partnerProperty">The navigation property this is a silent partner of.</param>
 /// <param name="propertyDeleteAction">The on delete action for this side of the association</param>
 /// <param name="multiplicity">The multiplicity of this side of the association.</param>
 /// <param name="name">The name of this navigation property.</param>
 public MetadataProviderEdmSilentNavigationProperty(IEdmNavigationProperty partnerProperty, EdmOnDeleteAction propertyDeleteAction, EdmMultiplicity multiplicity, string name)
 {
     this.partner = partnerProperty;
     this.deleteAction = propertyDeleteAction;
     this.name = name;
     switch (multiplicity)
     {
         case EdmMultiplicity.One:
             this.type = new EdmEntityTypeReference(this.partner.DeclaringEntityType(), false);
             break;
         case EdmMultiplicity.ZeroOrOne:
             this.type = new EdmEntityTypeReference(this.partner.DeclaringEntityType(), true);
             break;
         case EdmMultiplicity.Many:
             this.type = new EdmCollectionTypeReference(new EdmCollectionType(new EdmEntityTypeReference(this.partner.DeclaringEntityType(), false)));
             break;
     }
 }
        public void TestInitialize()
        {
            this.testModel = Test.OData.Utils.Metadata.TestModels.BuildTestModel();

            this.defaultContainer = this.testModel.EntityContainer;

            this.personSet = this.defaultContainer.FindEntitySet("Persons");
            this.personType = (IEdmEntityType)this.testModel.FindType("TestModel.Person");
            this.employeeType = (IEdmEntityType)this.testModel.FindType("TestModel.Employee");
            this.officeType = (IEdmEntityType)this.testModel.FindType("TestModel.OfficeType");
            this.addressType = (IEdmComplexType)this.testModel.FindType("TestModel.Address");
            this.metropolitanCitySet = this.defaultContainer.FindEntitySet("MetropolitanCities");
            this.metropolitanCityType = (IEdmEntityType)this.testModel.FindType("TestModel.MetropolitanCityType");
            this.boss = this.defaultContainer.FindSingleton("Boss");
            this.containedOfficeNavigationProperty = (IEdmNavigationProperty)this.metropolitanCityType.FindProperty("ContainedOffice");
            this.containedOfficeSet = (IEdmContainedEntitySet)metropolitanCitySet.FindNavigationTarget(this.containedOfficeNavigationProperty);
            this.containedMetropolitanCityNavigationProperty = (IEdmNavigationProperty)this.officeType.FindProperty("ContainedCity");
            this.containedMetropolitanCitySet = (IEdmContainedEntitySet)containedOfficeSet.FindNavigationTarget(this.containedMetropolitanCityNavigationProperty);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="NavigationPathSegment" /> class.
        /// </summary>
        /// <param name="previous">The property being accessed by this segment.</param>
        /// <param name="navigationProperty">The navigation property being accessed by this segment.</param>
        public NavigationPathSegment(ODataPathSegment previous, IEdmNavigationProperty navigationProperty)
            : base(previous)
        {
            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigation");
            }

            if (previous.EntitySet == null)
            {
                throw Error.Argument(SRResources.PreviousSegmentMustHaveEntitySet);
            }

            EdmType = navigationProperty.Partner.Multiplicity() == EdmMultiplicity.Many ?
                (IEdmType)navigationProperty.ToEntityType().GetCollection() :
                (IEdmType)navigationProperty.ToEntityType();
            EntitySet = previous.EntitySet.FindNavigationTarget(navigationProperty);
            NavigationProperty = navigationProperty;
        }
        internal static Uri GenerateNavigationPropertyLink(EntityInstanceContext entityContext, IEdmNavigationProperty navigationProperty, EntitySetConfiguration configuration, bool includeCast)
        {
            List<ODataPathSegment> navigationPathSegments = new List<ODataPathSegment>();
            navigationPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
            navigationPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext, configuration.EntityType)));

            if (includeCast)
            {
                navigationPathSegments.Add(new CastPathSegment(entityContext.EntityType));
            }

            navigationPathSegments.Add(new NavigationPathSegment(navigationProperty));

            string link = entityContext.UrlHelper.ODataLink(entityContext.PathHandler, navigationPathSegments);

            if (link == null)
            {
                return null;
            }

            return new Uri(link);
        }
        /// <summary>
        /// Reads entity reference links for a collection navigation link in request.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the entity reference links.</param>
        /// <param name="isExpanded">true if the navigation link is expanded.</param>
        /// <returns>The navigation link info for the entity reference links read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadEntityReferenceLinksForCollectionNavigationLinkInRequest(
            IODataJsonLightReaderEntryState entryState,
            IEdmNavigationProperty navigationProperty,
            bool isExpanded)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

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

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            LinkedList<ODataEntityReferenceLink> entityReferenceLinksList = null;
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataBind:
                            ODataEntityReferenceLink entityReferenceLink = propertyAnnotation.Value as ODataEntityReferenceLink;
                            if (entityReferenceLink != null)
                            {
                                throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_StringValueForCollectionBindPropertyAnnotation(navigationLink.Name, ODataAnnotationNames.ODataBind));
                            }

                            Debug.Assert(
                                propertyAnnotation.Value is LinkedList<ODataEntityReferenceLink> && propertyAnnotation.Value != null,
                                "The value of odata.bind property annotation must be either ODataEntityReferenceLink or List<ODataEntityReferenceLink>");
                            entityReferenceLinksList = (LinkedList<ODataEntityReferenceLink>)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedNavigationLinkInRequestPropertyAnnotation(
                                navigationLink.Name,
                                propertyAnnotation.Key,
                                ODataAnnotationNames.ODataBind));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateCollectionEntityReferenceLinksInfo(navigationLink, navigationProperty, entityReferenceLinksList, isExpanded);
        }
Beispiel #26
0
        /// <summary>
        /// Generate a SubExpand based on the current nav property and the curren token
        /// </summary>
        /// <param name="currentNavProp">the current navigation property</param>
        /// <param name="tokenIn">the current token</param>
        /// <returns>a new SelectExpand clause bound to the current token and nav prop</returns>
        private SelectExpandClause GenerateSubExpand(IEdmNavigationProperty currentNavProp, ExpandTermToken tokenIn)
        {
            SelectExpandBinder nextLevelBinder = new SelectExpandBinder(this.Configuration, currentNavProp.ToEntityType(), this.NavigationSource != null ? this.NavigationSource.FindNavigationTarget(currentNavProp) : null);

            return(nextLevelBinder.BindSubLevel(tokenIn.ExpandOption));
        }
Beispiel #27
0
        /// <summary>
        /// Parse from levelsOption token to LevelsClause.
        /// Negative value would be treated as max.
        /// </summary>
        /// <param name="levelsOption">The levelsOption for current expand.</param>
        /// <param name="sourceType">The type of current level navigation source.</param>
        /// <param name="property">Navigation property for current expand.</param>
        /// <returns>The LevelsClause parsed, null if levelsOption is null</returns>
        private static LevelsClause ParseLevels(long?levelsOption, IEdmType sourceType, IEdmNavigationProperty property)
        {
            if (!levelsOption.HasValue)
            {
                return(null);
            }

            IEdmType relatedType = property.ToEntityType();

            if (sourceType != null && relatedType != null && !UriEdmHelpers.IsRelatedTo(sourceType, relatedType))
            {
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_LevelsNotAllowedOnIncompatibleRelatedType(property.Name, relatedType.FullTypeName(), sourceType.FullTypeName()));
            }

            return(new LevelsClause(levelsOption.Value < 0, levelsOption.Value));
        }
        /// <summary>
        /// Constructs a NavigationLink for a particular <see cref="EntityInstanceContext" />, <see cref="IEdmNavigationProperty" /> and <see cref="ODataMetadataLevel" />.
        /// </summary>
        public virtual Uri BuildNavigationLink(EntityInstanceContext instanceContext, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel)
        {
            if (instanceContext == null)
            {
                throw Error.ArgumentNull("instanceContext");
            }

            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigationProperty");
            }

            NavigationLinkBuilder navigationLinkBuilder;

            if (!_navigationPropertyLinkBuilderLookup.TryGetValue(navigationProperty, out navigationLinkBuilder))
            {
                throw Error.Argument("navigationProperty", SRResources.NoNavigationLinkFactoryFound, navigationProperty.Name, navigationProperty.DeclaringEntityType(), _entitySetName);
            }
            Contract.Assert(navigationLinkBuilder != null);

            if (IsDefaultOrFull(metadataLevel) ||
                (IsMinimal(metadataLevel) && !navigationLinkBuilder.FollowsConventions))
            {
                return(navigationLinkBuilder.Factory(instanceContext, navigationProperty));
            }
            else
            {
                // client can infer it and didn't ask for it.
                return(null);
            }
        }
Beispiel #29
0
        /// <summary>
        /// Generate an expand item (and a select item for the implicit nav prop if necessary) based on an ExpandTermToken
        /// </summary>
        /// <param name="tokenIn">the expandTerm token to visit</param>
        /// <returns>the expand item for this expand term token.</returns>
        private SelectItem GenerateExpandItem(ExpandTermToken tokenIn)
        {
            ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn");

            // ensure that we're always dealing with proper V4 syntax
            if (tokenIn.PathToNavProp.NextToken != null && !tokenIn.PathToNavProp.IsNamespaceOrContainerQualified())
            {
                if (tokenIn.PathToNavProp.NextToken.Identifier != UriQueryConstants.RefSegment || tokenIn.PathToNavProp.NextToken.NextToken != null)
                {
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
                }
            }

            PathSegmentToken currentToken = tokenIn.PathToNavProp;

            IEdmStructuredType      currentLevelEntityType = this.EdmType;
            List <ODataPathSegment> pathSoFar         = new List <ODataPathSegment>();
            PathSegmentToken        firstNonTypeToken = currentToken;

            if (currentToken.IsNamespaceOrContainerQualified())
            {
                pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelEntityType, out firstNonTypeToken));
            }

            IEdmProperty edmProperty = this.configuration.Resolver.ResolveProperty(currentLevelEntityType, firstNonTypeToken.Identifier);

            if (edmProperty == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.ODataFullName(), currentToken.Identifier));
            }

            IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty;

            if (currentNavProp == null)
            {
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationProperty(currentToken.Identifier, currentLevelEntityType.ODataFullName()));
            }

            bool isRef = false;

            if (firstNonTypeToken.NextToken != null)
            {
                // lastly... make sure that, since we're on a NavProp, that the next token isn't null.
                if (firstNonTypeToken.NextToken.Identifier == UriQueryConstants.RefSegment)
                {
                    isRef = true;
                }
                else
                {
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
                }
            }

            pathSoFar.Add(new NavigationPropertySegment(currentNavProp, /*entitySet*/ null));
            ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar);

            IEdmNavigationSource targetNavigationSource = null;

            if (this.NavigationSource != null)
            {
                targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp);
            }

            // call MetadataBinder to build the filter clause
            FilterClause filterOption = null;

            if (tokenIn.FilterOption != null)
            {
                MetadataBinder binder       = this.BuildNewMetadataBinder(targetNavigationSource);
                FilterBinder   filterBinder = new FilterBinder(binder.Bind, binder.BindingState);
                filterOption = filterBinder.BindFilter(tokenIn.FilterOption);
            }

            // call MetadataBinder again to build the orderby clause
            OrderByClause orderbyOption = null;

            if (tokenIn.OrderByOptions != null)
            {
                MetadataBinder binder        = this.BuildNewMetadataBinder(targetNavigationSource);
                OrderByBinder  orderByBinder = new OrderByBinder(binder.Bind);
                orderbyOption = orderByBinder.BindOrderBy(binder.BindingState, tokenIn.OrderByOptions);
            }

            SearchClause searchOption = null;

            if (tokenIn.SearchOption != null)
            {
                MetadataBinder binder       = this.BuildNewMetadataBinder(targetNavigationSource);
                SearchBinder   searchBinder = new SearchBinder(binder.Bind);
                searchOption = searchBinder.BindSearch(tokenIn.SearchOption);
            }

            if (isRef)
            {
                return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption));
            }

            SelectExpandClause subSelectExpand;

            if (tokenIn.ExpandOption != null)
            {
                subSelectExpand = this.GenerateSubExpand(currentNavProp, tokenIn);
            }
            else
            {
                subSelectExpand = BuildDefaultSubExpand();
            }

            subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption);

            LevelsClause levelsOption = this.ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp);

            return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption));
        }
Beispiel #30
0
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup <string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            HttpMethod     requestMethod = controllerContext.Request.Method;
            IHttpRouteData routeData     = controllerContext.RouteData;

            if (!IsSupportedRequestMethod(requestMethod))
            {
                return(null);
            }

            if (odataPath.PathTemplate == "~/entityset/key/navigation/$ref" ||
                odataPath.PathTemplate == "~/entityset/key/cast/navigation/$ref" ||
                odataPath.PathTemplate == "~/singleton/navigation/$ref" ||
                odataPath.PathTemplate == "~/singleton/cast/navigation/$ref")
            {
                NavigationPathSegment  navigationSegment  = (NavigationPathSegment)odataPath.Segments[odataPath.Segments.Count - 2];
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType         declaringType      = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        routeData.Values[ODataRouteConstants.Key] = ((KeyValuePathSegment)odataPath.Segments[1]).Value;
                    }

                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    return(refActionName);
                }
            }
            else if ((requestMethod == HttpMethod.Delete) && (
                         odataPath.PathTemplate == "~/entityset/key/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/entityset/key/cast/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/singleton/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/singleton/cast/navigation/key/$ref"))
            {
                NavigationPathSegment  navigationSegment  = (NavigationPathSegment)odataPath.Segments[odataPath.Segments.Count - 3];
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType         declaringType      = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        routeData.Values[ODataRouteConstants.Key] = ((KeyValuePathSegment)odataPath.Segments[1]).Value;
                    }

                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    routeData.Values[ODataRouteConstants.RelatedKey]         = ((KeyValuePathSegment)odataPath.Segments[odataPath.Segments.Count - 2]).Value;
                    return(refActionName);
                }
            }

            return(null);
        }
Beispiel #31
0
        /// <summary>
        /// Find the navigation target which is <paramref name="navigationProperty"/> of current <paramref name="navigationSource"/> targets.
        /// The function is specifically used in Uri parser.
        /// </summary>
        /// <param name="navigationSource">The navigation source to find.</param>
        /// <param name="navigationProperty">The navigation property</param>
        /// <param name="matchBindingPath">The function used to determine if the binding path matches.</param>
        /// <param name="parsedSegments">The parsed segments in path, which is used to match binding path.</param>
        /// <param name="bindingPath">The output binding path of the navigation property which matches the <paramref name="parsedSegments"/></param>
        /// <returns>The navigation target which matches the binding path.</returns>
        public static IEdmNavigationSource FindNavigationTarget(this IEdmNavigationSource navigationSource, IEdmNavigationProperty navigationProperty, Func <IEdmPathExpression, IList <ODataPathSegment>, bool> matchBindingPath, IList <ODataPathSegment> parsedSegments, out IEdmPathExpression bindingPath)
        {
            Debug.Assert(navigationSource != null);
            Debug.Assert(navigationProperty != null);
            Debug.Assert(matchBindingPath != null);
            Debug.Assert(parsedSegments != null);

            bindingPath = null;

            if (navigationProperty.ContainsTarget)
            {
                return(navigationSource.FindNavigationTarget(navigationProperty));
            }

            IEnumerable <IEdmNavigationPropertyBinding> bindings = navigationSource.FindNavigationPropertyBindings(navigationProperty);

            if (bindings != null)
            {
                foreach (var binding in bindings)
                {
                    if (matchBindingPath(binding.Path, parsedSegments))
                    {
                        bindingPath = binding.Path;
                        return(binding.Target);
                    }
                }
            }

            return(new UnknownEntitySet(navigationSource, navigationProperty));
        }
Beispiel #32
0
        private void ValidateRestrictions(
            int?remainDepth,
            int currentDepth,
            SelectExpandClause selectExpandClause,
            IEdmNavigationProperty navigationProperty,
            ODataValidationSettings validationSettings)
        {
            IEdmModel edmModel = _selectExpandQueryOption.Context.Model;
            int?      depth    = remainDepth;

            if (remainDepth < 0)
            {
                throw new ODataException(
                          Error.Format(SRResources.MaxExpandDepthExceeded, currentDepth - 1, "MaxExpansionDepth"));
            }

            IEdmProperty       pathProperty;
            IEdmStructuredType pathStructuredType;

            if (navigationProperty == null)
            {
                pathProperty       = _selectExpandQueryOption.Context.TargetProperty;
                pathStructuredType = _selectExpandQueryOption.Context.TargetStructuredType;
            }
            else
            {
                pathProperty       = navigationProperty;
                pathStructuredType = navigationProperty.ToEntityType();
            }

            foreach (SelectItem selectItem in selectExpandClause.SelectedItems)
            {
                ExpandedNavigationSelectItem expandItem = selectItem as ExpandedNavigationSelectItem;
                if (expandItem != null)
                {
                    NavigationPropertySegment navigationSegment =
                        (NavigationPropertySegment)expandItem.PathToNavigationProperty.LastSegment;
                    IEdmNavigationProperty property = navigationSegment.NavigationProperty;
                    if (EdmLibHelpers.IsNotExpandable(property, edmModel))
                    {
                        throw new ODataException(Error.Format(SRResources.NotExpandablePropertyUsedInExpand,
                                                              property.Name));
                    }

                    if (edmModel != null)
                    {
                        ValidateOtherQueryOptionInExpand(property, edmModel, expandItem, validationSettings);
                        bool isExpandable;
                        ExpandConfiguration expandConfiguration;
                        isExpandable = EdmLibHelpers.IsExpandable(property.Name,
                                                                  pathProperty,
                                                                  pathStructuredType,
                                                                  edmModel,
                                                                  out expandConfiguration);
                        if (isExpandable)
                        {
                            int maxDepth = expandConfiguration.MaxDepth;
                            if (maxDepth > 0 && (remainDepth == null || maxDepth < remainDepth))
                            {
                                remainDepth = maxDepth;
                            }
                        }
                        else if (!isExpandable)
                        {
                            if (!_defaultQuerySettings.EnableExpand ||
                                (expandConfiguration != null && expandConfiguration.ExpandType == SelectExpandType.Disabled))
                            {
                                throw new ODataException(Error.Format(SRResources.NotExpandablePropertyUsedInExpand,
                                                                      property.Name));
                            }
                        }
                    }

                    if (remainDepth.HasValue)
                    {
                        remainDepth--;
                        if (expandItem.LevelsOption != null)
                        {
                            ValidateLevelsOption(expandItem.LevelsOption, remainDepth.Value, currentDepth + 1, edmModel,
                                                 property);
                        }
                    }

                    ValidateRestrictions(remainDepth, currentDepth + 1, expandItem.SelectAndExpand, property,
                                         validationSettings);
                    remainDepth = depth;
                }

                ValidateSelectItem(selectItem, pathProperty, pathStructuredType, edmModel);
            }
        }
Beispiel #33
0
            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="model">The model used to resolve the metadata.</param>
            /// <param name="payloadElement">The payload element to get the reader metadata for.</param>
            public ReaderMetadata(IEdmModel model, ODataPayloadElement payloadElement)
            {
                var expectedTypeAnnotation = payloadElement.GetAnnotation <ExpectedTypeODataPayloadElementAnnotation>();

                // NOTE: we don't require a model for the computation of the expected type (since the expected one might be a primitive type).
                this.expectedType = GetExpectedType(expectedTypeAnnotation, model);

                // We need a model for all the other expected reader metadata
                if (model == null)
                {
                    // If the annotation specified some model dependent data (basically anything but primitive expected type)
                    // and we don't have a model, we wouldn't be able to correctly represent it here (since we need the model to resolve these)
                    // and thus we should not pass in the expected type alone, as that would be changing the intent of the annotation.
                    if (expectedTypeAnnotation != null &&
                        (expectedTypeAnnotation.EntitySet != null ||
                         expectedTypeAnnotation.EdmEntitySet != null ||
                         expectedTypeAnnotation.FunctionImport != null ||
                         expectedTypeAnnotation.ProductFunctionImport != null ||
                         expectedTypeAnnotation.MemberProperty != null ||
                         expectedTypeAnnotation.EdmProperty != null ||
                         expectedTypeAnnotation.NavigationProperty != null ||
                         expectedTypeAnnotation.EdmNavigationProperty != null ||
                         expectedTypeAnnotation.OpenMemberPropertyName != null ||
                         expectedTypeAnnotation.OwningType != null ||
                         expectedTypeAnnotation.EdmOwningType != null))
                    {
                        this.expectedType = null;
                    }

                    return;
                }

                ODataPayloadElementType elementType = payloadElement.ElementType;

                switch (elementType)
                {
                case ODataPayloadElementType.EntityInstance:        // fall through
                case ODataPayloadElementType.EntitySetInstance:
                    this.entitySet = GetExpectedEntitySet(expectedTypeAnnotation, model, payloadElement);
                    break;

                case ODataPayloadElementType.DeferredLink:          // fall through
                case ODataPayloadElementType.LinkCollection:
                    this.navigationProperty = GetExpectedNavigationProperty(expectedTypeAnnotation, model);
                    break;

                case ODataPayloadElementType.PrimitiveMultiValueProperty:       // fall through
                case ODataPayloadElementType.PrimitiveProperty:                 // fall through
                case ODataPayloadElementType.ComplexProperty:                   // fall through
                case ODataPayloadElementType.ComplexMultiValueProperty:         // fall through
                case ODataPayloadElementType.EmptyCollectionProperty:
                    this.structuralProperty = GetExpectedStructuralProperty(expectedTypeAnnotation, model);
                    this.functionImport     = GetExpectedFunctionImport(expectedTypeAnnotation, model);
                    break;

                case ODataPayloadElementType.ComplexInstanceCollection:         // fall through
                case ODataPayloadElementType.PrimitiveCollection:               // fall through
                case ODataPayloadElementType.EmptyUntypedCollection:
                    this.functionImport = GetExpectedFunctionImport(expectedTypeAnnotation, model);
                    break;

                case ODataPayloadElementType.ComplexInstance:
                    // NOTE: this is how we model parameter payloads
                    this.functionImport = GetExpectedFunctionImport(expectedTypeAnnotation, model);
                    break;
                }
            }
Beispiel #34
0
            private static Uri BuildNavigationNextPageLink(ODataResource entry, ExpandedNavigationSelectItem expandedNavigationSelectItem)
            {
                var segment = (NavigationPropertySegment)expandedNavigationSelectItem.PathToNavigationProperty.LastSegment;
                ResourceRangeVariableReferenceNode refNode            = OeGetParser.CreateRangeVariableReferenceNode((IEdmEntitySet)segment.NavigationSource);
                IEdmNavigationProperty             navigationProperty = segment.NavigationProperty;

                var keys = new List <KeyValuePair <IEdmStructuralProperty, Object> >();

                if (navigationProperty.IsPrincipal())
                {
                    IEnumerator <IEdmStructuralProperty> dependentProperties = navigationProperty.Partner.DependentProperties().GetEnumerator();
                    foreach (IEdmStructuralProperty key in navigationProperty.Partner.PrincipalProperties())
                    {
                        foreach (ODataProperty property in entry.Properties)
                        {
                            if (property.Name == key.Name)
                            {
                                dependentProperties.MoveNext();
                                keys.Add(new KeyValuePair <IEdmStructuralProperty, Object>(dependentProperties.Current, property.Value));
                                break;
                            }
                        }
                    }
                }
                else
                {
                    IEnumerator <IEdmStructuralProperty> principalProperties = navigationProperty.PrincipalProperties().GetEnumerator();
                    foreach (IEdmStructuralProperty key in navigationProperty.DependentProperties())
                    {
                        foreach (ODataProperty property in entry.Properties)
                        {
                            if (property.Name == key.Name)
                            {
                                principalProperties.MoveNext();
                                keys.Add(new KeyValuePair <IEdmStructuralProperty, Object>(principalProperties.Current, property.Value));
                                break;
                            }
                        }
                    }
                }

                BinaryOperatorNode filterExpression = OeGetParser.CreateFilterExpression(refNode, keys);

                if (expandedNavigationSelectItem.FilterOption != null)
                {
                    filterExpression = new BinaryOperatorNode(BinaryOperatorKind.And, filterExpression, expandedNavigationSelectItem.FilterOption.Expression);
                }

                var segments = new ODataPathSegment[] { new EntitySetSegment((IEdmEntitySet)refNode.NavigationSource) };

                var odataUri = new ODataUri()
                {
                    Path            = new ODataPath(segments),
                    Filter          = new FilterClause(filterExpression, refNode.RangeVariable),
                    OrderBy         = expandedNavigationSelectItem.OrderByOption,
                    SelectAndExpand = expandedNavigationSelectItem.SelectAndExpand,
                    Top             = expandedNavigationSelectItem.TopOption,
                    Skip            = expandedNavigationSelectItem.SkipOption,
                    QueryCount      = expandedNavigationSelectItem.CountOption
                };

                return(odataUri.BuildUri(ODataUrlKeyDelimiter.Parentheses));
            }
        /// <summary>
        /// Finds the bindings of the navigation property.
        /// </summary>
        /// <param name="navigationProperty">The navigation property.</param>
        /// <returns>The list of bindings for current navigation property.</returns>
        public virtual IEnumerable <IEdmNavigationPropertyBinding> FindNavigationPropertyBindings(IEdmNavigationProperty navigationProperty)
        {
            EdmUtil.CheckArgumentNull(navigationProperty, "navigationProperty");

            Dictionary <string, IEdmNavigationPropertyBinding> result;

            if (this.navigationPropertyMappings.TryGetValue(navigationProperty, out result))
            {
                return(result.Select(item => item.Value));
            }

            return(null);
        }
Beispiel #36
0
        // Process $levels in ExpandedNavigationSelectItem.
        private ExpandedNavigationSelectItem ProcessLevels(
            ExpandedNavigationSelectItem expandItem,
            int levelsMaxLiteralExpansionDepth,
            ModelBoundQuerySettings querySettings,
            out bool levelsEncounteredInExpand,
            out bool isMaxLevelInExpand)
        {
            int level;

            isMaxLevelInExpand = false;

            if (expandItem.LevelsOption == null)
            {
                levelsEncounteredInExpand = false;
                level = 1;
            }
            else
            {
                levelsEncounteredInExpand = true;
                if (expandItem.LevelsOption.IsMaxLevel)
                {
                    isMaxLevelInExpand = true;
                    level = levelsMaxLiteralExpansionDepth;
                }
                else
                {
                    level = (int)expandItem.LevelsOption.Level;
                }
            }

            // Do not expand when:
            // 1. $levels is equal to or less than 0.
            // 2. $levels value is greater than current MaxExpansionDepth
            if (level <= 0 || level > levelsMaxLiteralExpansionDepth)
            {
                return(null);
            }

            ExpandedNavigationSelectItem item = null;
            SelectExpandClause           currentSelectExpandClause = null;
            SelectExpandClause           selectExpandClause        = null;
            bool levelsEncounteredInInnerExpand = false;
            bool isMaxLevelInInnerExpand        = false;
            var  entityType = expandItem.NavigationSource.EntityType();
            IEdmNavigationProperty navigationProperty =
                (expandItem.PathToNavigationProperty.LastSegment as NavigationPropertySegment).NavigationProperty;
            ModelBoundQuerySettings nestQuerySettings = EdmHelpers.GetModelBoundQuerySettings(navigationProperty,
                                                                                              navigationProperty.ToEntityType(),
                                                                                              Context.Model);

            // Try different expansion depth until expandItem.SelectAndExpand is successfully expanded
            while (selectExpandClause == null && level > 0)
            {
                selectExpandClause = ProcessLevels(
                    expandItem.SelectAndExpand,
                    levelsMaxLiteralExpansionDepth - level,
                    nestQuerySettings,
                    out levelsEncounteredInInnerExpand,
                    out isMaxLevelInInnerExpand);
                level--;
            }

            if (selectExpandClause == null)
            {
                return(null);
            }

            // Correct level value
            level++;
            List <SelectItem> originAutoSelectItems;
            List <SelectItem> originAutoExpandItems;

            int maxDepth = GetMaxExpandDepth(querySettings, navigationProperty.Name);

            if (maxDepth == 0 || levelsMaxLiteralExpansionDepth > maxDepth)
            {
                maxDepth = levelsMaxLiteralExpansionDepth;
            }

            GetAutoSelectExpandItems(
                entityType,
                Context.Model,
                expandItem.NavigationSource,
                selectExpandClause.AllSelected,
                nestQuerySettings,
                maxDepth - 1,
                out originAutoSelectItems,
                out originAutoExpandItems);
            if (expandItem.SelectAndExpand.SelectedItems.Any(it => it is PathSelectItem))
            {
                originAutoSelectItems.Clear();
            }

            if (level > 1)
            {
                RemoveSameExpandItem(navigationProperty, originAutoExpandItems);
            }

            List <SelectItem> autoExpandItems = new List <SelectItem>(originAutoExpandItems);
            bool hasAutoSelectExpandInExpand  = (originAutoSelectItems.Count + originAutoExpandItems.Count != 0);
            bool allSelected = originAutoSelectItems.Count == 0 && selectExpandClause.AllSelected;

            while (level > 0)
            {
                autoExpandItems = RemoveExpandItemExceedMaxDepth(maxDepth - level, originAutoExpandItems);
                if (item == null)
                {
                    if (hasAutoSelectExpandInExpand)
                    {
                        currentSelectExpandClause = new SelectExpandClause(
                            Array.Empty <SelectItem>().Concat(selectExpandClause.SelectedItems)
                            .Concat(originAutoSelectItems).Concat(autoExpandItems),
                            allSelected);
                    }
                    else
                    {
                        currentSelectExpandClause = selectExpandClause;
                    }
                }
                else if (selectExpandClause.AllSelected)
                {
                    // Concat the processed items
                    currentSelectExpandClause = new SelectExpandClause(
                        new SelectItem[] { item }.Concat(selectExpandClause.SelectedItems)
                        .Concat(originAutoSelectItems).Concat(autoExpandItems),
                        allSelected);
                }
                else
                {
                    // PathSelectItem is needed for the expanded item if AllSelected is false.
                    PathSelectItem pathSelectItem = new PathSelectItem(
                        new ODataSelectPath(expandItem.PathToNavigationProperty));

                    // Keep default SelectItems before expanded item to keep consistent with normal SelectExpandClause
                    SelectItem[] items = new SelectItem[] { item, pathSelectItem };
                    currentSelectExpandClause = new SelectExpandClause(
                        Array.Empty <SelectItem>().Concat(selectExpandClause.SelectedItems)
                        .Concat(items)
                        .Concat(originAutoSelectItems).Concat(autoExpandItems),
                        allSelected);
                }

                // Construct a new ExpandedNavigationSelectItem with current SelectExpandClause.
                item = new ExpandedNavigationSelectItem(
                    expandItem.PathToNavigationProperty,
                    expandItem.NavigationSource,
                    currentSelectExpandClause,
                    expandItem.FilterOption,
                    expandItem.OrderByOption,
                    expandItem.TopOption,
                    expandItem.SkipOption,
                    expandItem.CountOption,
                    expandItem.SearchOption,
                    null,
                    expandItem.ComputeOption,
                    expandItem.ApplyOption);

                level--;

                // Need expand and construct selectExpandClause every time if it is max level in inner expand
                if (isMaxLevelInInnerExpand)
                {
                    selectExpandClause = ProcessLevels(
                        expandItem.SelectAndExpand,
                        levelsMaxLiteralExpansionDepth - level,
                        nestQuerySettings,
                        out levelsEncounteredInInnerExpand,
                        out isMaxLevelInInnerExpand);
                }
            }

            levelsEncounteredInExpand = levelsEncounteredInExpand || levelsEncounteredInInnerExpand || hasAutoSelectExpandInExpand;
            isMaxLevelInExpand        = isMaxLevelInExpand || isMaxLevelInInnerExpand;

            return(item);
        }
        /// <summary>
        /// Reads expanded entry navigation link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the expanded link.</param>
        /// <returns>The navigation link info for the expanded link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadExpandedEntryNavigationLink(IODataJsonLightReaderEntryState entryState, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

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

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataContext:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.context annotation should have been parsed as a non-null Uri.");
                            navigationLink.ContextUrl = (Uri)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedExpandedSingletonNavigationLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateExpandedEntryLinkInfo(navigationLink, navigationProperty);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ODataSerializerContext"/> class.
        /// </summary>
        /// <param name="entity">The entity whose navigation property is being expanded.</param>
        /// <param name="selectExpandClause">The <see cref="SelectExpandClause"/> for the navigation property being expanded.</param>
        /// <param name="navigationProperty">The navigation property being expanded.</param>
        /// <remarks>This constructor is used to construct the serializer context for writing expanded properties.</remarks>
        public ODataSerializerContext(EntityInstanceContext entity, SelectExpandClause selectExpandClause, IEdmNavigationProperty navigationProperty)
        {
            if (entity == null)
            {
                throw Error.ArgumentNull("entity");
            }
            if (navigationProperty == null)
            {
                throw Error.ArgumentNull("navigationProperty");
            }

            ODataSerializerContext context = entity.SerializerContext;

            Request          = context.Request;
            RequestContext   = context.RequestContext;
            Url              = context.Url;
            NavigationSource = context.NavigationSource;
            Model            = context.Model;
            Path             = context.Path;
            RootElementName  = context.RootElementName;
            SkipExpensiveAvailabilityChecks = context.SkipExpensiveAvailabilityChecks;
            MetadataLevel = context.MetadataLevel;
            Items         = context.Items;

            ExpandedEntity     = entity;
            SelectExpandClause = selectExpandClause;
            NavigationProperty = navigationProperty;

            NavigationSource = context.NavigationSource.FindNavigationTarget(navigationProperty);
        }
Beispiel #39
0
        static string GetPathForEntityNavigationPropertyEntityRemove(string entityPath, IEdmNavigationProperty navProp)
        {
            var key = navProp.ToEntityType().Key().First();

            entityPath = entityPath + "/" + navProp.Name + "/{refId}/$ref";
            return(entityPath);
        }
Beispiel #40
0
 static string GetPathForEntityNavigationPropertyEntityAdd(string entityPath, IEdmNavigationProperty navProp)
 {
     entityPath = entityPath + "/" + navProp.Name + "/$ref";
     return(entityPath);
 }
Beispiel #41
0
        /// <summary>
        /// Binds a <see cref="InnerPathToken"/>.
        /// This includes more than just navigations - it includes complex property access and primitive collections.
        /// </summary>
        /// <param name="segmentToken">The segment token to bind.</param>
        /// <returns>The bound node.</returns>
        internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken)
        {
            FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod, state);

            // First we get the parent node
            QueryNode parent = this.DetermineParentNode(segmentToken, state);

            Debug.Assert(parent != null, "parent should never be null");

            SingleValueNode singleValueParent = parent as SingleValueNode;

            if (singleValueParent == null)
            {
                QueryNode boundFunction;
                if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction))
                {
                    return(boundFunction);
                }

                throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Identifier));
            }

            // Using the parent and name of this token, we try to get the IEdmProperty it represents
            IEdmProperty property = BindProperty(singleValueParent.TypeReference, segmentToken.Identifier, this.Resolver);

            if (property == null)
            {
                QueryNode boundFunction;
                if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction))
                {
                    return(boundFunction);
                }

                if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpen())
                {
                    throw new ODataException(
                              ODataErrorStrings.MetadataBinder_PropertyNotDeclared(
                                  parent.GetEdmTypeReference().FullName(), segmentToken.Identifier));
                }

                return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier));
            }

            IEdmStructuralProperty structuralProperty = property as IEdmStructuralProperty;

            if (property.Type.IsComplex())
            {
                // Generate a segment to parsed segments for the parsed token
                state.ParsedSegments.Add(new PropertySegment(structuralProperty));
                return(new SingleComplexNode(singleValueParent as SingleResourceNode, property));
            }
            else if (property.Type.IsPrimitive())
            {
                return(new SingleValuePropertyAccessNode(singleValueParent, property));
            }

            // Note - this means nonentity collection (primitive or complex)
            if (property.Type.IsNonEntityCollectionType())
            {
                if (property.Type.IsStructuredCollectionType())
                {
                    // Generate a segment to parsed segments for the parsed token
                    state.ParsedSegments.Add(new PropertySegment(structuralProperty));
                    return(new CollectionComplexNode(singleValueParent as SingleResourceNode, property));
                }

                return(new CollectionPropertyAccessNode(singleValueParent, property));
            }

            IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty;

            if (navigationProperty == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name));
            }

            SingleResourceNode parentResource = EnsureParentIsResourceForNavProp(singleValueParent);

            IEdmNavigationSource navigationSource;
            QueryNode            node = GetNavigationNode(navigationProperty, parentResource, segmentToken.NamedValues, state,
                                                          new KeyBinder(this.bindMethod), out navigationSource);

            // Generate a segment to parsed segments for the parsed token
            state.ParsedSegments.Add(new NavigationPropertySegment(navigationProperty, navigationSource));

            return(node);
        }
        private static string FindRefActionName(ILookup<string, HttpActionDescriptor> actionMap,
            IEdmNavigationProperty navigationProperty, IEdmEntityType declaringType, HttpMethod method)
        {
            string actionNamePrefix;
            if (method == HttpMethod.Delete)
            {
                actionNamePrefix = DeleteRefActionNamePrefix;
            }
            else if (method == HttpMethod.Get)
            {
                actionNamePrefix = GetRefActionNamePrefix;
            }
            else
            {
                actionNamePrefix = CreateRefActionNamePrefix;
            }

            // Examples: CreateRefToOrdersFromCustomer, CreateRefToOrders, CreateRef.
            return actionMap.FindMatchingAction(
                        actionNamePrefix + "To" + navigationProperty.Name + "From" + declaringType.Name,
                        actionNamePrefix + "To" + navigationProperty.Name,
                        actionNamePrefix);
        }
        private static MethodCallExpression CreateWhereExpression(Expression source, Expression subquery, IEdmNavigationProperty edmNavigationProperty)
        {
            Type             subqueryType      = OeExpressionHelper.GetCollectionItemType(subquery.Type);
            var              subqueryParameter = Expression.Parameter(subqueryType, subqueryType.Name);
            BinaryExpression joinExpression    = GetJoinExpression(source, subqueryParameter, edmNavigationProperty);
            LambdaExpression predicate         = Expression.Lambda(joinExpression, subqueryParameter);

            MethodInfo whereMethodInfo = OeMethodInfoHelper.GetWhereMethodInfo(subqueryType);

            return(Expression.Call(whereMethodInfo, subquery, predicate));
        }
Beispiel #44
0
 /// <summary>
 /// Adds a navigation target, specifying the destination entity set of a navigation property of an entity in this navigation source.
 /// </summary>
 /// <param name="property">The navigation property the target is being set for.</param>
 /// <param name="target">The destination navigation source of the specified navigation property.</param>
 public void AddNavigationTarget(IEdmNavigationProperty property, IEdmNavigationSource target)
 {
     this.navigationPropertyMappings[property] = target;
     this.navigationTargetsCache.Clear(null);
 }
        private static BinaryExpression GetJoinExpression(Expression source, ParameterExpression subqueryParameter, IEdmNavigationProperty edmNavigationProperty)
        {
            IEnumerable <IEdmStructuralProperty> sourceProperties;
            IEnumerable <IEdmStructuralProperty> subqueryProperties;

            if (edmNavigationProperty.IsPrincipal())
            {
                sourceProperties   = edmNavigationProperty.Partner.PrincipalProperties();
                subqueryProperties = edmNavigationProperty.Partner.DependentProperties();
            }
            else
            {
                if (edmNavigationProperty.Type.IsCollection())
                {
                    sourceProperties   = edmNavigationProperty.PrincipalProperties();
                    subqueryProperties = edmNavigationProperty.DependentProperties();
                }
                else
                {
                    sourceProperties   = edmNavigationProperty.DependentProperties();
                    subqueryProperties = edmNavigationProperty.PrincipalProperties();
                }
            }

            BinaryExpression?joinExpression = null;
            IEnumerator <IEdmStructuralProperty>?sourceEnumerator   = null;
            IEnumerator <IEdmStructuralProperty>?subqueryEnumerator = null;

            try
            {
                sourceEnumerator   = sourceProperties.GetEnumerator();
                subqueryEnumerator = subqueryProperties.GetEnumerator();
                while (sourceEnumerator.MoveNext())
                {
                    subqueryEnumerator.MoveNext();

                    IEdmStructuralProperty sourceKeyEdmProperty   = sourceEnumerator.Current;
                    IEdmStructuralProperty subqueryKeyEdmProperty = subqueryEnumerator.Current;

                    PropertyInfo sourceKeyClrProperty   = source.Type.GetPropertyIgnoreCase(sourceKeyEdmProperty);
                    PropertyInfo subqueryKeyClrProperty = subqueryParameter.Type.GetPropertyIgnoreCase(subqueryKeyEdmProperty);

                    Expression sourceKeyExpression   = Expression.Property(source, sourceKeyClrProperty);
                    Expression subqueryKeyExpression = Expression.Property(subqueryParameter, subqueryKeyClrProperty);
                    if (sourceKeyExpression.Type != subqueryKeyExpression.Type)
                    {
                        subqueryKeyExpression = Expression.Convert(subqueryKeyExpression, sourceKeyExpression.Type);
                    }

                    BinaryExpression equalsExpression = Expression.Equal(sourceKeyExpression, subqueryKeyExpression);
                    joinExpression = joinExpression == null ? equalsExpression : Expression.AndAlso(joinExpression, equalsExpression);
                }
            }
            finally
            {
                if (sourceEnumerator != null)
                {
                    sourceEnumerator.Dispose();
                }
                if (subqueryEnumerator != null)
                {
                    subqueryEnumerator.Dispose();
                }
            }

            return(joinExpression !);
        }
        public NodeToExpressionTranslatorTests()
        {
            this.functionExpressionBinder = new FunctionExpressionBinder(t => { throw new Exception(); });

            this.customerResourceType = new ResourceType(typeof(Customer), ResourceTypeKind.EntityType, null, "Fake", "Customer", false)
            {
                IsOpenType = true
            };
            var derivedCustomerResourceType = new ResourceType(typeof(DerivedCustomer), ResourceTypeKind.EntityType, this.customerResourceType, "Fake", "DerivedCustomer", false);

            this.weaklyBackedDerivedType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, derivedCustomerResourceType, "Fake", "WeaklyBackedCustomer", false)
            {
                CanReflectOnInstanceType = false
            };
            var nameResourceProperty = new ResourceProperty("Name", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)));

            this.customerResourceType.AddProperty(nameResourceProperty);
            var addressResourceType     = new ResourceType(typeof(Address), ResourceTypeKind.ComplexType, null, "Fake", "Address", false);
            var addressResourceProperty = new ResourceProperty("Address", ResourcePropertyKind.ComplexType, addressResourceType);

            this.customerResourceType.AddProperty(addressResourceProperty);

            var namesResourceProperty = new ResourceProperty("Names", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(ResourceType.GetPrimitiveResourceType(typeof(string))));

            this.customerResourceType.AddProperty(namesResourceProperty);
            var addressesResourceProperty = new ResourceProperty("Addresses", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(addressResourceType));

            this.customerResourceType.AddProperty(addressesResourceProperty);

            var bestFriendResourceProperty = new ResourceProperty("BestFriend", ResourcePropertyKind.ResourceReference, this.customerResourceType);

            this.customerResourceType.AddProperty(bestFriendResourceProperty);
            var otherFriendsResourceProperty = new ResourceProperty("OtherFriends", ResourcePropertyKind.ResourceSetReference, this.customerResourceType);

            this.customerResourceType.AddProperty(otherFriendsResourceProperty);

            this.weaklyBackedResourceProperty = new ResourceProperty("WeaklyBacked", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string)))
            {
                CanReflectOnInstanceTypeProperty = false
            };

            var guid1ResourceProperty         = new ResourceProperty("Guid1", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid)));
            var guid2ResourceProperty         = new ResourceProperty("Guid2", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid)));
            var nullableGuid1ResourceProperty = new ResourceProperty("NullableGuid1", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid?)));
            var nullableGuid2ResourceProperty = new ResourceProperty("NullableGuid2", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(Guid?)));

            this.customerResourceType.AddProperty(guid1ResourceProperty);
            this.customerResourceType.AddProperty(guid2ResourceProperty);
            this.customerResourceType.AddProperty(nullableGuid1ResourceProperty);
            this.customerResourceType.AddProperty(nullableGuid2ResourceProperty);

            var resourceSet = new ResourceSet("Customers", this.customerResourceType);

            resourceSet.SetReadOnly();
            var resourceSetWrapper = ResourceSetWrapper.CreateForTests(resourceSet, EntitySetRights.All);

            this.model = new EdmModel();

            this.customerEdmType = new MetadataProviderEdmEntityType("Fake", this.customerResourceType, null, false, true, false, t => {});
            this.model.AddElement(this.customerEdmType);

            this.derivedCustomerEdmType = new MetadataProviderEdmEntityType("Fake", derivedCustomerResourceType, this.customerEdmType, false, false, false, t => { });
            this.model.AddElement(this.derivedCustomerEdmType);

            this.weaklyBackedCustomerEdmType = new MetadataProviderEdmEntityType("Fake", weaklyBackedDerivedType, this.derivedCustomerEdmType, false, false, false, t => { });
            this.model.AddElement(this.weaklyBackedCustomerEdmType);

            this.nameProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, nameResourceProperty, EdmCoreModel.Instance.GetString(true), null);
            this.customerEdmType.AddProperty(this.nameProperty);

            var addressEdmType = new MetadataProviderEdmComplexType("Fake", addressResourceType, null, false, false, t => {});

            this.model.AddElement(addressEdmType);

            this.addressProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, addressResourceProperty, new EdmComplexTypeReference(addressEdmType, true), null);
            this.customerEdmType.AddProperty(this.addressProperty);

            this.namesProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, namesResourceProperty, new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetString(false))), null);
            this.customerEdmType.AddProperty(this.namesProperty);

            this.addressesProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, addressesResourceProperty, new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(addressEdmType, false))), null);
            this.customerEdmType.AddProperty(this.addressesProperty);

            this.bestFriendNavigation = new MetadataProviderEdmNavigationProperty(this.customerEdmType, bestFriendResourceProperty, new EdmEntityTypeReference(this.customerEdmType, true));
            this.customerEdmType.AddProperty(this.bestFriendNavigation);

            this.otherFriendsNavigation = new MetadataProviderEdmNavigationProperty(this.customerEdmType, otherFriendsResourceProperty, new EdmCollectionTypeReference(new EdmCollectionType(new EdmEntityTypeReference(this.customerEdmType, true))));
            this.customerEdmType.AddProperty(this.otherFriendsNavigation);

            this.weaklyBackedProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, this.weaklyBackedResourceProperty, EdmCoreModel.Instance.GetString(true), null);
            this.customerEdmType.AddProperty(this.weaklyBackedProperty);

            var guid1EdmProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, guid1ResourceProperty, EdmCoreModel.Instance.GetGuid(false), null);

            this.customerEdmType.AddProperty(guid1EdmProperty);
            var guid2EdmProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, guid2ResourceProperty, EdmCoreModel.Instance.GetGuid(false), null);

            this.customerEdmType.AddProperty(guid2EdmProperty);
            var nullableGuid1EdmProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, nullableGuid1ResourceProperty, EdmCoreModel.Instance.GetGuid(true), null);

            this.customerEdmType.AddProperty(nullableGuid1EdmProperty);
            var nullableGuid2EdmProperty = new MetadataProviderEdmStructuralProperty(this.customerEdmType, nullableGuid2ResourceProperty, EdmCoreModel.Instance.GetGuid(true), null);

            this.customerEdmType.AddProperty(nullableGuid2EdmProperty);

            this.entitySet = new EdmEntitySetWithResourceSet(new EdmEntityContainer("Fake", "Container"), resourceSetWrapper, this.customerEdmType);
            ((EdmEntitySet)this.entitySet).AddNavigationTarget(this.bestFriendNavigation, this.entitySet);
            ((EdmEntitySet)this.entitySet).AddNavigationTarget(this.otherFriendsNavigation, this.entitySet);

            this.model.SetAnnotationValue(this.customerEdmType, this.customerResourceType);
            this.model.SetAnnotationValue(this.derivedCustomerEdmType, derivedCustomerResourceType);
            this.model.SetAnnotationValue(this.weaklyBackedCustomerEdmType, this.weaklyBackedDerivedType);
            this.model.SetAnnotationValue(this.nameProperty, nameResourceProperty);
            this.model.SetAnnotationValue(addressEdmType, addressResourceType);
            this.model.SetAnnotationValue(this.addressProperty, addressResourceProperty);
            this.model.SetAnnotationValue(this.namesProperty, namesResourceProperty);
            this.model.SetAnnotationValue(this.addressesProperty, addressesResourceProperty);
            this.model.SetAnnotationValue(this.bestFriendNavigation, bestFriendResourceProperty);
            this.model.SetAnnotationValue(this.otherFriendsNavigation, otherFriendsResourceProperty);
            this.model.SetAnnotationValue(this.weaklyBackedProperty, this.weaklyBackedResourceProperty);
            this.model.SetAnnotationValue(this.entitySet, resourceSetWrapper);
            this.model.SetAnnotationValue(guid1EdmProperty, guid1ResourceProperty);
            this.model.SetAnnotationValue(guid2EdmProperty, guid2ResourceProperty);
            this.model.SetAnnotationValue(nullableGuid1EdmProperty, nullableGuid1ResourceProperty);
            this.model.SetAnnotationValue(nullableGuid2EdmProperty, nullableGuid2ResourceProperty);

            this.testSubject = this.CreateTestSubject();
        }
Beispiel #47
0
        /// <summary>
        /// Generates a navigation link following the OData URL conventions for the entity represented by <paramref name="entityContext"/> and the given
        /// navigation property.
        /// </summary>
        /// <param name="entityContext">The <see cref="EntityInstanceContext"/> representing the entity for which the navigation link needs to be generated.</param>
        /// <param name="navigationProperty">The EDM navigation property.</param>
        /// <param name="includeCast">Represents whether the generated link should have a cast segment representing a type cast.</param>
        /// <returns>The navigation link following the OData URL conventions.</returns>
        public static Uri GenerateNavigationPropertyLink(this EntityInstanceContext entityContext, IEdmNavigationProperty navigationProperty, bool includeCast)
        {
            if (entityContext == null)
            {
                throw Error.ArgumentNull("entityContext");
            }
            if (entityContext.Url == null)
            {
                throw Error.Argument("entityContext", SRResources.UrlHelperNull, typeof(EntityInstanceContext).Name);
            }

            List <ODataPathSegment> navigationPathSegments = new List <ODataPathSegment>();

            navigationPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
            navigationPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext)));

            if (includeCast)
            {
                navigationPathSegments.Add(new CastPathSegment(entityContext.EntityType));
            }

            navigationPathSegments.Add(new NavigationPathSegment(navigationProperty));

            string link = entityContext.Url.CreateODataLink(navigationPathSegments);

            if (link == null)
            {
                return(null);
            }

            return(new Uri(link));
        }
Beispiel #48
0
 /// <summary>
 /// Binds a <see cref="IEdmNavigationProperty"/> to create a LINQ <see cref="Expression"/> that
 /// represents the semantics of the <see cref="IEdmNavigationProperty"/>.
 /// </summary>
 /// <param name="sourceNode">The node that represents the navigation source.</param>
 /// <param name="navigationProperty">The navigation property to bind.</param>
 /// <returns>The LINQ <see cref="Expression"/> created.</returns>
 public virtual Expression BindNavigationPropertyNode(QueryNode sourceNode, IEdmNavigationProperty navigationProperty)
 {
     return(BindNavigationPropertyNode(sourceNode, navigationProperty, null));
 }
 /// <summary>
 /// Finds the bindings of the navigation property.
 /// </summary>
 /// <param name="navigationProperty">The navigation property.</param>
 /// <returns>The list of bindings for current navigation property.</returns>
 public override IEnumerable <IEdmNavigationPropertyBinding> FindNavigationPropertyBindings(IEdmNavigationProperty navigationProperty)
 {
     return(this.parentNavigationSource.FindNavigationPropertyBindings(navigationProperty));
 }
Beispiel #50
0
        private Expression BuildPropertyContainer(IEdmEntityType elementType, Expression source,
                                                  Dictionary <IEdmNavigationProperty, ExpandedNavigationSelectItem> propertiesToExpand,
                                                  ISet <IEdmStructuralProperty> propertiesToInclude, ISet <IEdmStructuralProperty> autoSelectedProperties, bool isSelectingOpenTypeSegments)
        {
            IList <NamedPropertyExpression> includedProperties = new List <NamedPropertyExpression>();

            foreach (KeyValuePair <IEdmNavigationProperty, ExpandedNavigationSelectItem> kvp in propertiesToExpand)
            {
                IEdmNavigationProperty       propertyToExpand = kvp.Key;
                ExpandedNavigationSelectItem expandItem       = kvp.Value;
                SelectExpandClause           projection       = expandItem.SelectAndExpand;

                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToExpand, source);
                Expression propertyValue = CreatePropertyValueExpressionWithFilter(elementType, propertyToExpand, source,
                                                                                   expandItem.FilterOption);
                Expression nullCheck = GetNullCheckExpression(propertyToExpand, propertyValue, projection);

                Expression countExpression = CreateTotalCountExpression(propertyValue, expandItem);

                // projection can be null if the expanded navigation property is not further projected or expanded.
                if (projection != null)
                {
                    propertyValue = ProjectAsWrapper(propertyValue, projection, propertyToExpand.ToEntityType(), expandItem.NavigationSource as IEdmEntitySet,
                                                     expandItem);
                }

                NamedPropertyExpression propertyExpression = new NamedPropertyExpression(propertyName, propertyValue);
                if (projection != null)
                {
                    if (!propertyToExpand.Type.IsCollection())
                    {
                        propertyExpression.NullCheck = nullCheck;
                    }
                    else if (_settings.PageSize != null)
                    {
                        propertyExpression.PageSize = _settings.PageSize.Value;
                    }
                    propertyExpression.TotalCount  = countExpression;
                    propertyExpression.CountOption = expandItem.CountOption;
                }

                includedProperties.Add(propertyExpression);
            }

            foreach (IEdmStructuralProperty propertyToInclude in propertiesToInclude)
            {
                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToInclude, source);
                Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source);
                includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue));
            }

            foreach (IEdmStructuralProperty propertyToInclude in autoSelectedProperties)
            {
                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToInclude, source);
                Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source);
                includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue)
                {
                    AutoSelected = true
                });
            }

            if (isSelectingOpenTypeSegments)
            {
                var dynamicPropertyDictionary = EdmLibHelpers.GetDynamicPropertyDictionary(elementType, _model);

                Expression propertyName          = Expression.Constant(dynamicPropertyDictionary.Name);
                Expression propertyValue         = Expression.Property(source, dynamicPropertyDictionary.Name);
                Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue);
                if (_settings.HandleNullPropagation == HandleNullPropagationOption.True)
                {
                    // source == null ? null : propertyValue
                    propertyValue = Expression.Condition(
                        test: Expression.Equal(source, Expression.Constant(value: null)),
                        ifTrue: Expression.Constant(value: null, type: propertyValue.Type.ToNullable()),
                        ifFalse: nullablePropertyValue);
                }
                else
                {
                    propertyValue = nullablePropertyValue;
                }

                includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue));
            }

            // create a property container that holds all these property names and values.
            return(PropertyContainer.CreatePropertyContainer(includedProperties));
        }
Beispiel #51
0
        /// <summary>
        /// Generate a SubExpand based on the current nav property and the curren token
        /// </summary>
        /// <param name="currentNavProp">the current navigation property</param>
        /// <param name="tokenIn">the current token</param>
        /// <returns>a new SelectExpand clause bound to the current token and nav prop</returns>
        protected override SelectExpandClause GenerateSubExpand(IEdmNavigationProperty currentNavProp, ExpandTermToken tokenIn)
        {
            ExpandBinder nextLevelBinder = new ExpandOptionExpandBinder(this.Configuration, currentNavProp.ToEntityType(), this.EntitySet != null ? this.EntitySet.FindNavigationTarget(currentNavProp) : null);

            return(nextLevelBinder.Bind(tokenIn.ExpandOption));
        }
Beispiel #52
0
 /// <summary>
 /// Asynchronously read a top-level entity reference link.
 /// </summary>
 /// <param name="navigationProperty">The navigation property for which to read the entity reference link.</param>
 /// <returns>Task which when completed returns an <see cref="ODataEntityReferenceLink"/> representing the read entity reference link.</returns>
 internal virtual Task <ODataEntityReferenceLink> ReadEntityReferenceLinkAsync(IEdmNavigationProperty navigationProperty)
 {
     DebugUtils.CheckNoExternalCallers();
     throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.EntityReferenceLink);
 }
        /// <summary>
        /// Reads the information of a deferred link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationPropertyName">The name of the navigation property for which to read the deferred link.</param>
        /// <param name="navigationProperty">The navigation property for which to read the deferred link. This can be null.</param>
        /// <returns>Returns the navigation link info for the deferred navigation link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadDeferredNavigationLink(IODataJsonLightReaderEntryState entryState, string navigationPropertyName, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(!string.IsNullOrEmpty(navigationPropertyName), "!string.IsNullOrEmpty(navigationPropertyName)");
            Debug.Assert(navigationProperty == null || navigationPropertyName == navigationProperty.Name, "navigationProperty == null || navigationPropertyName == navigationProperty.Name");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationPropertyName,
                IsCollection = navigationProperty == null ? null : (bool?)navigationProperty.Type.IsCollection()
            };

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedDeferredLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateDeferredLinkInfo(navigationLink, navigationProperty);
        }
Beispiel #54
0
        /// <summary>
        /// Decorate an expand tree using a select token.
        /// </summary>
        /// <param name="subExpand">the already built sub expand</param>
        /// <param name="currentNavProp">the current navigation property</param>
        /// <param name="select">the select token to use</param>
        /// <returns>A new SelectExpand clause decorated with the select token.</returns>
        protected override SelectExpandClause DecorateExpandWithSelect(SelectExpandClause subExpand, IEdmNavigationProperty currentNavProp, SelectToken select)
        {
            SelectBinder selectBinder = new SelectBinder(this.Model, currentNavProp.ToEntityType(), this.Settings.SelectExpandLimit, subExpand);

            return(selectBinder.Bind(select));
        }
        /// <summary>
        /// Reads expanded feed navigation link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the expanded link.</param>
        /// <returns>The navigation link info for the expanded link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadExpandedFeedNavigationLink(IODataJsonLightReaderEntryState entryState, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

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

            ODataFeed expandedFeed = new ODataFeed();

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataNextLink:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.nextLink annotation should have been parsed as a non-null Uri.");
                            expandedFeed.NextPageLink = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataCount:
                            Debug.Assert(propertyAnnotation.Value is long && propertyAnnotation.Value != null, "The odata.count annotation should have been parsed as a non-null long.");
                            expandedFeed.Count = (long?)propertyAnnotation.Value;
                            break;
                        case ODataAnnotationNames.ODataContext:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.context annotation should have been parsed as a non-null Uri.");
                            navigationLink.ContextUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataDeltaLink:   // Delta links are not supported on expanded feeds.
                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedExpandedCollectionNavigationLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateExpandedFeedLinkInfo(navigationLink, navigationProperty, expandedFeed);
        }
Beispiel #56
0
        public void EntityReferenceLinksPropertyAccessOrderTest()
        {
            //// NOTE: this tests is important as Astoria relies on this behavior. Astoria only provides a next link after all the entity reference
            ////       links have been written so we must not access the next link before that point.

            ODataEntityReferenceLink entityReferenceLink1 = new ODataEntityReferenceLink {
                Url = new Uri("http://odata.org/linkresult1")
            };
            ODataEntityReferenceLink entityReferenceLink2 = new ODataEntityReferenceLink {
                Url = new Uri("http://odata.org/linkresult2")
            };
            ODataEntityReferenceLink entityReferenceLink3 = new ODataEntityReferenceLink {
                Url = new Uri("http://odata.org/linkresult3")
            };

            Uri  nextPageLink          = new Uri("http://odata.org/nextpage");
            Uri  incorrectNextPageLink = new Uri("http://odata.org/incorrectnextpage");
            long correctCountValue     = 3;

            // the expected result instance
            ODataEntityReferenceLinks expectedResult = new ODataEntityReferenceLinks
            {
                Count        = 3,
                Links        = new ODataEntityReferenceLink[] { entityReferenceLink1, entityReferenceLink2, entityReferenceLink3 },
                NextPageLink = nextPageLink
            };

            PayloadWriterTestDescriptor.WriterTestExpectedResultCallback expectedResultCallback = this.CreateExpectedCallback(expectedResult, /*forceNextLinkAndCountAtEnd*/ true);

            this.CombinatorialEngineProvider.RunCombinations(
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent.Where(tc => !tc.IsRequest),
                (testConfiguration) =>
            {
                testConfiguration = testConfiguration.Clone();
                testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                // The instance with the proper inline count set but an incorrect next link; as we enumerate the links themselves
                // we will invalidate the inline count and set the correct next link to guarantee the correct order of property accesses
                // NOTE: we need to create this new for each iteration since the checking enumerable can (intentionally) only be enumerated once.
                ODataEntityReferenceLinks testReferenceLink = new ODataEntityReferenceLinks
                {
                    Count = correctCountValue,
                    // In JSON lite, we will write the next link first if one is available.  Otherwise, we'll write it at the end.
                    NextPageLink = testConfiguration.Format == ODataFormat.Json ? null : incorrectNextPageLink
                };
                testReferenceLink.Links = new CheckingEntityReferenceLinkEnumerable(
                    testReferenceLink,
                    correctCountValue,
                    nextPageLink /* correct next link */,
                    entityReferenceLink1,
                    entityReferenceLink2,
                    entityReferenceLink3);

                PayloadWriterTestDescriptor <ODataEntityReferenceLinks> testDescriptor =
                    new PayloadWriterTestDescriptor <ODataEntityReferenceLinks>(this.Settings, testReferenceLink, expectedResultCallback);

                IEdmNavigationProperty navProp = null;
                IEdmEntitySet entitySet        = null;
                // When writing JSON lite, always provide a model and a non-null nav prop.
                // The error cases when a model isn't provided or the nav prop is null are tested in JsonLightEntityReferenceLinkWriterTests
                if (testConfiguration.Format == ODataFormat.Json)
                {
                    testDescriptor.Model = CreateModelWithNavProps();
                    var edmModel         = testDescriptor.GetMetadataProvider();
                    navProp   = GetCollectionNavProp(edmModel);
                    entitySet = GetCollectionEntitySet(edmModel);
                }

                ODataEntityReferenceLinks entityReferenceLinks = testDescriptor.PayloadItems.Single();

                TestWriterUtils.WriteAndVerifyTopLevelContent(
                    testDescriptor,
                    testConfiguration,
                    (messageWriter) => messageWriter.WriteEntityReferenceLinks(entityReferenceLinks),
                    this.Assert,
                    baselineLogger: this.Logger);
            });
        }
Beispiel #57
0
 protected virtual void ProcessNavigationProperty(IEdmNavigationProperty property)
 {
     this.ProcessProperty(property);
 }
 /// <summary>
 /// Register a link builder for a <see cref="IEdmNavigationProperty" /> that navigates from Entities in this EntitySet.
 /// </summary>
 public void AddNavigationPropertyLinkBuilder(IEdmNavigationProperty navigationProperty, NavigationLinkBuilder linkBuilder)
 {
     _navigationPropertyLinkBuilderLookup[navigationProperty] = linkBuilder;
 }
        /// <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 #60
0
        /// <inheritdoc />
        public bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            Debug.Assert(context.Action != null);

            ActionModel action           = context.Action;
            string      actionMethodName = action.ActionName;

            // Need to refactor the following
            // for example:  CreateRef( with the navigation property parameter) should for all navigation properties
            // CreateRefToOrdersFromCustomer, CreateRefToOrders, CreateRef.
            string method = SplitRefActionName(actionMethodName, out string httpMethod, out string property, out string declaring);

            if (method == null || (property != null && property.Length == 0))
            {
                // Early return for the following cases: Get|Create|DeleteRefTo
                return(false);
            }

            IEdmNavigationSource navigationSource = context.NavigationSource;
            IEdmEntityType       entityType       = context.EntityType;

            // For entity set, we should have the key parameter
            // For Singleton, we should not have the key parameter
            bool hasODataKeyParameter = action.HasODataKeyParameter(entityType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false);

            if ((context.EntitySet != null && !hasODataKeyParameter) ||
                (context.Singleton != null && hasODataKeyParameter))
            {
                return(false);
            }

            // Find the navigation property declaring type
            IEdmStructuredType declaringType = entityType;

            if (declaring != null)
            {
                if (declaring.Length == 0)
                {
                    // Early return for the following cases: Get|Create|DeleteRefTo{NavigationProperty}From
                    return(false);
                }

                declaringType = entityType.FindTypeInInheritance(context.Model, declaring);
                if (declaringType == null)
                {
                    return(false);
                }
            }

            // Process the generic scenario
            if (property == null)
            {
                return(ProcessNonNavigationProperty(httpMethod, context, action, navigationSource, entityType, declaringType));
            }

            // Find the navigation property if have
            IEdmNavigationProperty navigationProperty = null;

            navigationProperty = declaringType.DeclaredNavigationProperties().FirstOrDefault(p => p.Name == property);

            if (navigationProperty == null)
            {
                return(false);
            }

            IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

            if (context.EntitySet != null)
            {
                segments.Add(new EntitySetSegmentTemplate(context.EntitySet));
                segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, context.EntitySet));
            }
            else
            {
                segments.Add(new SingletonSegmentTemplate(context.Singleton));
            }

            if (entityType != declaringType)
            {
                segments.Add(new CastSegmentTemplate(declaringType, entityType, navigationSource));
            }

            IEdmNavigationSource          targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _);
            NavigationLinkSegmentTemplate linkTemplate           = new NavigationLinkSegmentTemplate(navigationProperty, targetNavigationSource);

            IEdmEntityType navigationPropertyType            = navigationProperty.Type.GetElementTypeOrSelf().AsEntity().EntityDefinition();
            bool           hasNavigationPropertyKeyParameter = action.HasODataKeyParameter(navigationPropertyType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false, "relatedKey");

            if (hasNavigationPropertyKeyParameter)
            {
                linkTemplate.Key = KeySegmentTemplate.CreateKeySegment(navigationPropertyType, targetNavigationSource, "relatedKey");
            }
            else
            {
                hasNavigationPropertyKeyParameter = action.HasODataKeyParameter(navigationPropertyType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false, "relatedId");
                if (hasNavigationPropertyKeyParameter)
                {
                    linkTemplate.Key = KeySegmentTemplate.CreateKeySegment(navigationPropertyType, targetNavigationSource, "relatedId");
                }
            }

            segments.Add(linkTemplate);

            ODataPathTemplate template = new ODataPathTemplate(segments);

            action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions);

            // processed
            return(true);
        }