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; } }
/// <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); } }
/// <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); }
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); }
/// <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)); }
/// <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); } }
/// <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)); }
/// <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); }
/// <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)); }
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); } }
/// <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; } }
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); }
// 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); }
static string GetPathForEntityNavigationPropertyEntityRemove(string entityPath, IEdmNavigationProperty navProp) { var key = navProp.ToEntityType().Key().First(); entityPath = entityPath + "/" + navProp.Name + "/{refId}/$ref"; return(entityPath); }
static string GetPathForEntityNavigationPropertyEntityAdd(string entityPath, IEdmNavigationProperty navProp) { entityPath = entityPath + "/" + navProp.Name + "/$ref"; return(entityPath); }
/// <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)); }
/// <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(); }
/// <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> /// 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)); }
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)); }
/// <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)); }
/// <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); }
/// <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); }
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); }); }
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; }
/// <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); }