/// <summary> /// Validates a <see cref="CountQueryOption" />. /// </summary> /// <param name="countQueryOption">The $count query.</param> /// <param name="validationSettings">The validation settings.</param> public virtual void Validate(CountQueryOption countQueryOption, ODataValidationSettings validationSettings) { if (countQueryOption == null) { throw Error.ArgumentNull(nameof(countQueryOption)); } if (validationSettings == null) { throw Error.ArgumentNull(nameof(validationSettings)); } ODataPath path = countQueryOption.Context.Path; if (path != null && path.Count > 0) { IEdmProperty property = countQueryOption.Context.TargetProperty; IEdmStructuredType structuredType = countQueryOption.Context.TargetStructuredType; string name = countQueryOption.Context.TargetName; if (EdmHelpers.IsNotCountable(property, structuredType, countQueryOption.Context.Model, countQueryOption.Context.DefaultQuerySettings.EnableCount)) { if (property == null) { throw new InvalidOperationException(Error.Format(SRResources.NotCountableEntitySetUsedForCount, name)); } else { throw new InvalidOperationException(Error.Format(SRResources.NotCountablePropertyUsedForCount, name)); } } } }
/// <summary> /// Validates a <see cref="TopQueryOption" />. /// </summary> /// <param name="topQueryOption">The $top query.</param> /// <param name="validationSettings">The validation settings.</param> public virtual void Validate(TopQueryOption topQueryOption, ODataValidationSettings validationSettings) { if (topQueryOption == null) { throw Error.ArgumentNull(nameof(topQueryOption)); } if (validationSettings == null) { throw Error.ArgumentNull(nameof(validationSettings)); } if (topQueryOption.Value > validationSettings.MaxTop) { throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, validationSettings.MaxTop, AllowedQueryOptions.Top, topQueryOption.Value)); } int maxTop; IEdmProperty property = topQueryOption.Context.TargetProperty; IEdmStructuredType structuredType = topQueryOption.Context.TargetStructuredType; if (EdmHelpers.IsTopLimitExceeded( property, structuredType, topQueryOption.Context.Model, topQueryOption.Value, topQueryOption.Context.DefaultQuerySettings, out maxTop)) { throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topQueryOption.Value)); } }
public override SingleValueNode Visit(SingleValuePropertyAccessNode nodeIn) { if (nodeIn.Source != null) { if (nodeIn.Source.Kind == QueryNodeKind.SingleNavigationNode) { SingleNavigationNode singleNavigationNode = nodeIn.Source as SingleNavigationNode; if (EdmHelpers.IsNotSortable(nodeIn.Property, singleNavigationNode.NavigationProperty, singleNavigationNode.NavigationProperty.ToEntityType(), _model, _enableOrderBy)) { return(nodeIn); } } else if (nodeIn.Source.Kind == QueryNodeKind.SingleComplexNode) { SingleComplexNode singleComplexNode = nodeIn.Source as SingleComplexNode; if (EdmHelpers.IsNotSortable(nodeIn.Property, singleComplexNode.Property, nodeIn.Property.DeclaringType, _model, _enableOrderBy)) { return(nodeIn); } } else if (EdmHelpers.IsNotSortable(nodeIn.Property, _property, _structuredType, _model, _enableOrderBy)) { return(nodeIn); } } if (nodeIn.Source != null) { return(nodeIn.Source.Accept(this)); } return(null); }
private void ValidateLevelsOption(LevelsClause levelsClause, int depth, int currentDepth, IEdmModel edmModel, IEdmNavigationProperty property) { ExpandConfiguration expandConfiguration; bool isExpandable = EdmHelpers.IsExpandable(property.Name, property, property.ToEntityType(), edmModel, out expandConfiguration); if (isExpandable) { int maxDepth = expandConfiguration.MaxDepth; if (maxDepth > 0 && maxDepth < depth) { depth = maxDepth; } if ((depth == 0 && levelsClause.IsMaxLevel) || (depth < levelsClause.Level)) { throw new ODataException( Error.Format(SRResources.MaxExpandDepthExceeded, currentDepth + depth, "MaxExpansionDepth")); } } else { if (!_defaultQuerySettings.EnableExpand || (expandConfiguration != null && expandConfiguration.ExpandType == SelectExpandType.Disabled)) { throw new ODataException(Error.Format(SRResources.NotExpandablePropertyUsedInExpand, property.Name)); } } }
/// <summary> /// Get the page size. /// </summary> /// <param name="actionExecutedContext">The response value.</param> /// <param name="responseValue">The response value.</param> /// <param name="singleResultCollection">The content as SingleResult.Queryable.</param> /// <param name="actionDescriptor">The action context, i.e. action and controller name.</param> /// <param name="request">The request.</param> private void GetModelBoundPageSize( ActionExecutedContext actionExecutedContext, object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request) { ODataQueryContext queryContext; try { queryContext = GetODataQueryContext(responseValue, singleResultCollection, actionDescriptor, request); } catch (InvalidOperationException e) { actionExecutedContext.Result = CreateBadRequestResult(Error.Format(SRResources.UriQueryStringInvalid, e.Message), e); return; } ModelBoundQuerySettings querySettings = EdmHelpers.GetModelBoundQuerySettings(queryContext.TargetProperty, queryContext.TargetStructuredType, queryContext.Model); if (querySettings != null && querySettings.PageSize.HasValue) { _querySettings.ModelBoundPageSize = querySettings.PageSize; } }
/// <summary> /// Override this method for the navigation property node. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="sourceNode"></param> /// <param name="navigationProperty"></param> /// <param name="settings"></param> public virtual void ValidateNavigationPropertyNode(QueryNode sourceNode, IEdmNavigationProperty navigationProperty, ODataValidationSettings settings) { if (navigationProperty == null) { throw Error.ArgumentNull(nameof(navigationProperty)); } if (settings == null) { throw Error.ArgumentNull(nameof(settings)); } // Check whether the property is not filterable if (EdmHelpers.IsNotFilterable(navigationProperty, _property, _structuredType, _model, _defaultQuerySettings.EnableFilter)) { throw new ODataException(Error.Format(SRResources.NotFilterablePropertyUsedInFilter, navigationProperty.Name)); } // recursion if (sourceNode != null) { ValidateQueryNode(sourceNode, settings); } }
internal SelectExpandClause ProcessLevels() { bool levelsEncountered; bool isMaxLevel; ModelBoundQuerySettings querySettings = EdmHelpers.GetModelBoundQuerySettings(Context.TargetProperty, Context.TargetStructuredType, Context.Model, Context.DefaultQuerySettings); return(ProcessLevels(SelectExpandClause, LevelsMaxLiteralExpansionDepth < 0 ? ODataValidationSettings.DefaultMaxExpansionDepth : LevelsMaxLiteralExpansionDepth, querySettings, out levelsEncountered, out isMaxLevel)); }
public override SingleValueNode Visit(SingleComplexNode nodeIn) { if (EdmHelpers.IsNotSortable(nodeIn.Property, _property, _structuredType, _model, _enableOrderBy)) { return(nodeIn); } if (nodeIn.Source != null) { return(nodeIn.Source.Accept(this)); } return(null); }
private void ValidateSelectItem(SelectItem selectItem, IEdmProperty pathProperty, IEdmStructuredType pathStructuredType, IEdmModel edmModel) { PathSelectItem pathSelectItem = selectItem as PathSelectItem; if (pathSelectItem != null) { ODataPathSegment segment = pathSelectItem.SelectedPath.LastSegment; NavigationPropertySegment navigationPropertySegment = segment as NavigationPropertySegment; if (navigationPropertySegment != null) { IEdmNavigationProperty property = navigationPropertySegment.NavigationProperty; if (EdmHelpers.IsNotNavigable(property, edmModel)) { throw new ODataException(Error.Format(SRResources.NotNavigablePropertyUsedInNavigation, property.Name)); } } else { PropertySegment propertySegment = segment as PropertySegment; if (propertySegment != null) { if (EdmHelpers.IsNotSelectable(propertySegment.Property, pathProperty, pathStructuredType, edmModel, _defaultQuerySettings.EnableSelect)) { throw new ODataException(Error.Format(SRResources.NotSelectablePropertyUsedInSelect, propertySegment.Property.Name)); } } } } else { WildcardSelectItem wildCardSelectItem = selectItem as WildcardSelectItem; if (wildCardSelectItem != null) { foreach (var property in pathStructuredType.StructuralProperties()) { if (EdmHelpers.IsNotSelectable(property, pathProperty, pathStructuredType, edmModel, _defaultQuerySettings.EnableSelect)) { throw new ODataException(Error.Format(SRResources.NotSelectablePropertyUsedInSelect, property.Name)); } } } } }
private void ValidateCountInExpand(IEdmProperty property, IEdmStructuredType structuredType, IEdmModel edmModel, bool?countOption) { if (countOption == true) { if (EdmHelpers.IsNotCountable( property, structuredType, edmModel, _defaultQuerySettings.EnableCount)) { throw new ODataException(Error.Format(SRResources.NotCountablePropertyUsedForCount, property.Name)); } } }
/// <summary> /// Override this method to validate collection complex property accessor. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="collectionComplexNode"></param> /// <param name="settings"></param> protected virtual void ValidateCollectionComplexNode(CollectionComplexNode collectionComplexNode, ODataValidationSettings settings) { Contract.Assert(collectionComplexNode != null); Contract.Assert(settings != null); // Check whether the property is filterable. IEdmProperty property = collectionComplexNode.Property; if (EdmHelpers.IsNotFilterable(property, _property, _structuredType, _model, _defaultQuerySettings.EnableFilter)) { throw new ODataException(Error.Format(SRResources.NotFilterablePropertyUsedInFilter, property.Name)); } ValidateQueryNode(collectionComplexNode.Source, settings); }
/// <summary> /// Override this method to validate property accessor. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="propertyAccessNode"></param> /// <param name="settings"></param> public virtual void ValidateSingleValuePropertyAccessNode(SingleValuePropertyAccessNode propertyAccessNode, ODataValidationSettings settings) { if (propertyAccessNode == null) { throw Error.ArgumentNull(nameof(propertyAccessNode)); } if (settings == null) { throw Error.ArgumentNull(nameof(settings)); } // Check whether the property is filterable. IEdmProperty property = propertyAccessNode.Property; bool notFilterable = false; if (propertyAccessNode.Source != null) { if (propertyAccessNode.Source.Kind == QueryNodeKind.SingleNavigationNode) { SingleNavigationNode singleNavigationNode = propertyAccessNode.Source as SingleNavigationNode; notFilterable = EdmHelpers.IsNotFilterable(property, singleNavigationNode.NavigationProperty, singleNavigationNode.NavigationProperty.ToEntityType(), _model, _defaultQuerySettings.EnableFilter); } else if (propertyAccessNode.Source.Kind == QueryNodeKind.SingleComplexNode) { SingleComplexNode singleComplexNode = propertyAccessNode.Source as SingleComplexNode; notFilterable = EdmHelpers.IsNotFilterable(property, singleComplexNode.Property, property.DeclaringType, _model, _defaultQuerySettings.EnableFilter); } else { notFilterable = EdmHelpers.IsNotFilterable(property, _property, _structuredType, _model, _defaultQuerySettings.EnableFilter); } } if (notFilterable) { throw new ODataException(Error.Format(SRResources.NotFilterablePropertyUsedInFilter, property.Name)); } ValidateQueryNode(propertyAccessNode.Source, settings); }
/// <summary> /// Gets the type reference for the return type. /// </summary> /// <param name="type">The type.</param> /// <param name="model">The model.</param> /// <returns>An <see cref="IEdmTypeReference"/> implementation.</returns> public static IEdmTypeReference GetReturnTypeReference(this Type type, IEdmModel model) { // In case it is a nullable type, get the underlying type type = TypeHelper.GetUnderlyingTypeOrSelf(type); if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task <>)) { // if the action returns a Task<T>, map that to just be returning a T type = type.GetGenericArguments()[0]; } else if (type == typeof(Task)) { // if the action returns a concrete Task, map that to being a void return type. type = typeof(void); } return(EdmHelpers.GetTypeReference(type, model)); }
/// <summary> /// The type to get the primitive type reference. /// </summary> /// <param name="type">The clr type to get edm type reference.</param> /// <returns>The edm type reference for the clr type.</returns> public static EdmTypeReference GetPrimitiveTypeReference(this Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } // Only handle primitive type right now var primitiveTypeKind = EdmHelpers.GetPrimitiveTypeKind(type, out var isNullable); if (!primitiveTypeKind.HasValue) { return(null); } return(new EdmPrimitiveTypeReference( EdmCoreModel.Instance.GetPrimitiveType(primitiveTypeKind.Value), isNullable)); }
private string GetAutoSelectRawValue() { var selectRawValue = RawValues.Select; var autoSelectRawValue = String.Empty; IEdmEntityType baseEntityType = Context.TargetStructuredType as IEdmEntityType; if (String.IsNullOrEmpty(selectRawValue)) { var autoSelectProperties = EdmHelpers.GetAutoSelectProperties(Context.TargetProperty, Context.TargetStructuredType, Context.Model); foreach (var property in autoSelectProperties) { if (!String.IsNullOrEmpty(autoSelectRawValue)) { autoSelectRawValue += ","; } if (baseEntityType != null && property.DeclaringType != baseEntityType) { autoSelectRawValue += String.Format(CultureInfo.InvariantCulture, "{0}/", property.DeclaringType.FullTypeName()); } autoSelectRawValue += property.Name; } if (!String.IsNullOrEmpty(autoSelectRawValue)) { if (!String.IsNullOrEmpty(selectRawValue)) { selectRawValue = String.Format(CultureInfo.InvariantCulture, "{0},{1}", autoSelectRawValue, selectRawValue); } else { selectRawValue = autoSelectRawValue; } } } return(selectRawValue); }
private void ValidateTopInExpand(IEdmProperty property, IEdmStructuredType structuredType, IEdmModel edmModel, long?topOption) { if (topOption != null) { Contract.Assert(topOption.Value <= Int32.MaxValue); int maxTop; if (EdmHelpers.IsTopLimitExceeded( property, structuredType, edmModel, (int)topOption.Value, _defaultQuerySettings, out maxTop)) { throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, maxTop, AllowedQueryOptions.Top, topOption.Value)); } } }
private string GetAutoExpandRawValue() { var expandRawValue = RawValues.Expand; IEdmEntityType baseEntityType = Context.TargetStructuredType as IEdmEntityType; var autoExpandRawValue = String.Empty; var autoExpandNavigationProperties = EdmHelpers.GetAutoExpandNavigationProperties( Context.TargetProperty, Context.TargetStructuredType, Context.Model, !String.IsNullOrEmpty(RawValues.Select)); foreach (var property in autoExpandNavigationProperties) { if (!String.IsNullOrEmpty(autoExpandRawValue)) { autoExpandRawValue += ","; } if (property.DeclaringEntityType() != baseEntityType) { autoExpandRawValue += String.Format(CultureInfo.InvariantCulture, "{0}/", property.DeclaringEntityType().FullTypeName()); } autoExpandRawValue += property.Name; } if (!String.IsNullOrEmpty(autoExpandRawValue)) { if (!String.IsNullOrEmpty(expandRawValue)) { expandRawValue = String.Format(CultureInfo.InvariantCulture, "{0},{1}", autoExpandRawValue, expandRawValue); } else { expandRawValue = autoExpandRawValue; } } return(expandRawValue); }
private void GetPathContext() { if (Path != null) { IEdmProperty property; IEdmStructuredType structuredType; string name; EdmHelpers.GetPropertyAndStructuredTypeFromPath( Path, out property, out structuredType, out name); TargetProperty = property; TargetStructuredType = structuredType; TargetName = name; } else { TargetStructuredType = ElementType as IEdmStructuredType; } }
/// <summary> /// Override this method to validate collection complex property accessor. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="collectionComplexNode"></param> /// <param name="settings"></param> public virtual void ValidateCollectionComplexNode(CollectionComplexNode collectionComplexNode, ODataValidationSettings settings) { if (collectionComplexNode == null) { throw Error.ArgumentNull("collectionComplexNode"); } if (settings == null) { throw Error.ArgumentNull("settings"); } // Check whether the property is filterable. IEdmProperty property = collectionComplexNode.Property; if (EdmHelpers.IsNotFilterable(property, _property, _structuredType, _model, _defaultQuerySettings.EnableFilter)) { throw new ODataException(Error.Format(SRResources.NotFilterablePropertyUsedInFilter, property.Name)); } ValidateQueryNode(collectionComplexNode.Source, settings); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostEnvironment env, ILogger <Startup> logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseCors("CorsPolicy"); app.UseODataBatching(); var defaultODataBatchHander = new DefaultODataBatchHandler(); defaultODataBatchHander.MessageQuotas.MaxNestingDepth = 2; defaultODataBatchHander.MessageQuotas.MaxOperationsPerChangeset = 10; defaultODataBatchHander.MessageQuotas.MaxReceivedMessageSize = 100; app.UseRouting(); app.UseAuthorization(); //app.UseMvc(o => //{ // o.Select().Expand().Filter().OrderBy().Count(); // o.MapODataServiceRoute("odata", "odata", EdmHelpers.GetEdmModel(), // ODataHelper.CreateDefaultODataBatchHander(2, 5, 100)); //}); app.UseEndpoints(endpoints => { endpoints.Select().Expand().Filter().OrderBy().MaxTop(100).Count(); endpoints.MapODataRoute("odata", "api", EdmHelpers.GetEdmModel(), ODataHelper.CreateDefaultODataBatchHander(2, 5, 100)); endpoints.MapControllers(); }); // Enable middleware to serve generated Swagger as a JSON endpoint app.UseSwagger(); // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.) app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "BookLoan Catalog API"); c.RoutePrefix = string.Empty; } ); bool isInDockerContainer = (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true"); if (isInDockerContainer) { logger.LogInformation("Docker Db connection = " + Environment.GetEnvironmentVariable("DB_CONN_STR")); } using (var serviceScope = app.ApplicationServices.GetService <IServiceScopeFactory>().CreateScope()) { var context = serviceScope.ServiceProvider.GetRequiredService <ApplicationDbContext>(); context.Database.EnsureCreated(); // create database if not already created. //var config = app.ApplicationServices.GetService<AppConfiguration>(); //Initialize(context); } }
/// <summary> /// Determine if the /// </summary> /// <param name="responseValue">The response value.</param> /// <param name="singleResultCollection">The content as SingleResult.Queryable.</param> /// <param name="actionDescriptor">The action context, i.e. action and controller name.</param> /// <param name="request">The OData path.</param> /// <returns></returns> private static bool ContainsAutoSelectExpandProperty( object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request) { Type elementClrType = GetElementType(responseValue, singleResultCollection, actionDescriptor); IEdmModel model = GetModel(elementClrType, request, actionDescriptor); if (model == null) { throw Error.InvalidOperation(SRResources.QueryGetModelMustNotReturnNull); } ODataPath path = request.ODataFeature().Path; IEdmType edmType = model.GetTypeMappingCache().GetEdmType(elementClrType, model)?.Definition; IEdmEntityType baseEntityType = edmType as IEdmEntityType; IEdmStructuredType structuredType = edmType as IEdmStructuredType; IEdmProperty property = null; if (path != null) { string name; EdmHelpers.GetPropertyAndStructuredTypeFromPath(path, out property, out structuredType, out name); } if (baseEntityType != null) { List <IEdmEntityType> entityTypes = new List <IEdmEntityType>(); entityTypes.Add(baseEntityType); entityTypes.AddRange(EdmHelpers.GetAllDerivedEntityTypes(baseEntityType, model)); foreach (var entityType in entityTypes) { IEnumerable <IEdmNavigationProperty> navigationProperties = entityType == baseEntityType ? entityType.NavigationProperties() : entityType.DeclaredNavigationProperties(); if (navigationProperties != null) { if (navigationProperties.Any( navigationProperty => EdmHelpers.IsAutoExpand(navigationProperty, property, entityType, model))) { return(true); } } IEnumerable <IEdmStructuralProperty> properties = entityType == baseEntityType ? entityType.StructuralProperties() : entityType.DeclaredStructuralProperties(); if (properties != null) { foreach (var edmProperty in properties) { if (EdmHelpers.IsAutoSelect(edmProperty, property, entityType, model)) { return(true); } } } } } else if (structuredType != null) { IEnumerable <IEdmStructuralProperty> properties = structuredType.StructuralProperties(); if (properties != null) { foreach (var edmProperty in properties) { if (EdmHelpers.IsAutoSelect(edmProperty, property, structuredType, model)) { return(true); } } } } return(false); }
private void GetAutoSelectExpandItems( IEdmEntityType baseEntityType, IEdmModel model, IEdmNavigationSource navigationSource, bool isAllSelected, ModelBoundQuerySettings modelBoundQuerySettings, int depth, out List <SelectItem> autoSelectItems, out List <SelectItem> autoExpandItems) { autoSelectItems = new List <SelectItem>(); autoExpandItems = new List <SelectItem>(); var autoSelectProperties = EdmHelpers.GetAutoSelectProperties(null, baseEntityType, model, modelBoundQuerySettings); foreach (var autoSelectProperty in autoSelectProperties) { List <ODataPathSegment> pathSegments = new List <ODataPathSegment>() { new PropertySegment(autoSelectProperty) }; PathSelectItem pathSelectItem = new PathSelectItem( new ODataSelectPath(pathSegments)); autoSelectItems.Add(pathSelectItem); } depth--; if (depth < 0) { return; } var autoExpandNavigationProperties = EdmHelpers.GetAutoExpandNavigationProperties(null, baseEntityType, model, !isAllSelected, modelBoundQuerySettings); foreach (var navigationProperty in autoExpandNavigationProperties) { IEdmNavigationSource currentEdmNavigationSource = navigationSource.FindNavigationTarget(navigationProperty); if (currentEdmNavigationSource != null) { List <ODataPathSegment> pathSegments = new List <ODataPathSegment>() { new NavigationPropertySegment(navigationProperty, currentEdmNavigationSource) }; ODataExpandPath expandPath = new ODataExpandPath(pathSegments); SelectExpandClause selectExpandClause = new SelectExpandClause(new List <SelectItem>(), true); ExpandedNavigationSelectItem item = new ExpandedNavigationSelectItem(expandPath, currentEdmNavigationSource, selectExpandClause); modelBoundQuerySettings = EdmHelpers.GetModelBoundQuerySettings(navigationProperty, navigationProperty.ToEntityType(), model); List <SelectItem> nestedSelectItems; List <SelectItem> nestedExpandItems; int maxExpandDepth = GetMaxExpandDepth(modelBoundQuerySettings, navigationProperty.Name); if (maxExpandDepth != 0 && maxExpandDepth < depth) { depth = maxExpandDepth; } GetAutoSelectExpandItems( currentEdmNavigationSource.EntityType(), model, item.NavigationSource, true, modelBoundQuerySettings, depth, out nestedSelectItems, out nestedExpandItems); selectExpandClause = new SelectExpandClause(nestedSelectItems.Concat(nestedExpandItems), nestedSelectItems.Count == 0); item = new ExpandedNavigationSelectItem(expandPath, currentEdmNavigationSource, selectExpandClause); autoExpandItems.Add(item); if (!isAllSelected || autoSelectProperties.Any()) { PathSelectItem pathSelectItem = new PathSelectItem( new ODataSelectPath(pathSegments)); autoExpandItems.Add(pathSelectItem); } } } }
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 (EdmHelpers.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 = EdmHelpers.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); } }
// 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); }