/// <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));
                    }
                }
            }
        }
Example #2
0
        /// <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));
                }
            }
        }
Example #5
0
        /// <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));
                        }
                    }
                }
            }
        }
Example #10
0
 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));
         }
     }
 }
Example #11
0
        /// <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);
        }
Example #13
0
        /// <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));
        }
Example #14
0
        /// <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));
        }
Example #15
0
        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));
         }
     }
 }
Example #17
0
        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);
        }
Example #18
0
        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);
        }
Example #20
0
        // 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);
            }
        }
Example #21
0
        /// <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);
        }