/// <summary>
        /// Apply $top and $skip parameters to query.
        /// </summary>
        /// <typeparam name="T">The type param</typeparam>
        /// <param name="query">The OData aware query.</param>
        /// <param name="topText">$top parameter value</param>
        /// <param name="skipText">$skip parameter value</param>
        /// <param name="entitySetName">The entity set name.</param>
        /// <returns>The <see cref="ODataQuery{T}"/> query with applied $top and $skip parameters.</returns>
        public static ODataQuery <T> TopSkip <T>(this ODataQuery <T> query, string topText = null, string skipText = null, string entitySetName = null)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }

            ODataSettings settings = query.ServiceProvider.GetRequiredService <ODataSettings>();

            Dictionary <string, string> dictionary = new Dictionary <string, string>();

            if (topText != null)
            {
                dictionary.Add("$top", topText);
            }

            if (skipText != null)
            {
                dictionary.Add("$skip", skipText);
            }

            ODataQueryOptionParser queryOptionParser = GetParser(
                query,
                entitySetName,
                dictionary);

            long?skip = queryOptionParser.ParseSkip();
            long?top  = queryOptionParser.ParseTop();

            if (skip.HasValue || top.HasValue || settings.QuerySettings.PageSize.HasValue)
            {
                IQueryable <T> result = TopSkipHelper.ApplySkipWithValidation(query, skip, settings);
                if (top.HasValue)
                {
                    result = TopSkipHelper.ApplyTopWithValidation(result, top, settings);
                }
                else
                {
                    result = TopSkipHelper.ApplyTopWithValidation(result, settings.QuerySettings.PageSize, settings);
                }

                return(new ODataQuery <T>(result, query.ServiceProvider));
            }

            return(query);
        }
        public static ODataQueryOptionParser GetParser <T>(ODataQuery <T> query, string entitySetName, IDictionary <string, string> raws)
        {
            IEdmModel edmModel = query.EdmModel;

            if (entitySetName == null)
            {
                entitySetName = typeof(T).Name;
            }

            IEdmEntityContainer[] containers =
                edmModel.SchemaElements.Where(
                    e => e.SchemaElementKind == EdmSchemaElementKind.EntityContainer &&
                    (e as IEdmEntityContainer).FindEntitySet(entitySetName) != null)
                .OfType <IEdmEntityContainer>()
                .ToArray();

            if (containers.Length == 0)
            {
                throw new ArgumentException($"Unable to find {entitySetName} entity set in the model.",
                                            nameof(entitySetName));
            }

            if (containers.Length > 1)
            {
                throw new ArgumentException($"Entity Set {entitySetName} found more that 1 time",
                                            nameof(entitySetName));
            }

            IEdmEntitySet entitySet = containers.Single().FindEntitySet(entitySetName);

            if (entitySet == null)
            {
            }

            ODataPath path = new ODataPath(new EntitySetSegment(entitySet));

            ODataQueryOptionParser parser = new ODataQueryOptionParser(edmModel, path, raws, query.ServiceProvider);

            ODataSettings settings = query.ServiceProvider.GetRequiredService <ODataSettings>();

            // Workaround for strange behavior in QueryOptionsParserConfiguration constructor which set it to false always
            parser.Resolver.EnableCaseInsensitive = settings.EnableCaseInsensitive;

            return(parser);
        }
        /// <summary>
        /// Enable applying OData specific functions to query
        /// </summary>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <param name="configuration">
        /// The configuration action.
        /// </param>
        /// <param name="edmModel">
        /// The edm model.
        /// </param>
        /// <typeparam name="T">
        /// The query type param
        /// </typeparam>
        /// <returns>
        /// The OData aware <see cref="ODataQuery{T}"/> query.
        /// </returns>
        public static ODataQuery <T> OData <T>(this IQueryable <T> query, Action <ODataSettings> configuration = null, IEdmModel edmModel = null)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }

            if (edmModel == null)
            {
                ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
                builder.AddEntityType(typeof(T));
                builder.AddEntitySet(typeof(T).Name, new EntityTypeConfiguration(new ODataModelBuilder(), typeof(T)));
                edmModel = builder.GetEdmModel();
            }
            else
            {
                if (edmModel.SchemaElements.Count(e => e.SchemaElementKind == EdmSchemaElementKind.EntityContainer) == 0)
                {
                    throw new ArgumentException("Provided Entity Model have no IEdmEntityContainer", nameof(edmModel));
                }
            }

            ODataSettings settings = new ODataSettings();

            configuration?.Invoke(settings);

            ServiceContainer container = new ServiceContainer();

            container.AddService(typeof(IEdmModel), edmModel);
            container.AddService(typeof(ODataQuerySettings), settings.QuerySettings);
            container.AddService(typeof(ODataUriParserSettings), settings.ParserSettings);
            container.AddService(typeof(IAssembliesResolver), new DefaultAssembliesResolver());
            container.AddService(typeof(FilterBinder), new FilterBinder(container));
            container.AddService(typeof(ODataUriResolver), settings.Resolver ?? ODataSettings.DefaultResolver);
            container.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions);
            container.AddService(typeof(ODataSettings), settings);
            container.AddService(typeof(DefaultQuerySettings), settings.DefaultQuerySettings);
            container.AddService(typeof(SelectExpandQueryValidator), new SelectExpandQueryValidator(settings.DefaultQuerySettings));

            ODataQuery <T> dataQuery = new ODataQuery <T>(query, container);

            return(dataQuery);
        }
        /// <summary>
        /// Apply $filter parameter to query.
        /// </summary>
        /// <param name="query">
        /// The OData aware query.
        /// </param>
        /// <param name="filterText">
        /// The $filter parameter text.
        /// </param>
        /// <param name="entitySetName">
        /// The entity set name.
        /// </param>
        /// <typeparam name="T">
        /// The query type param
        /// </typeparam>
        /// <returns>
        /// The <see cref="ODataQuery{T}"/> query with applied filter parameter.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Argument Null Exception
        /// </exception>
        public static ODataQuery <T> Filter <T>(this ODataQuery <T> query, string filterText, string entitySetName = null)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (filterText == null)
            {
                throw new ArgumentNullException(nameof(filterText));
            }

            IEdmModel edmModel = query.EdmModel;

            ODataQueryOptionParser queryOptionParser = GetParser(
                query,
                entitySetName,
                new Dictionary <string, string> {
                { "$filter", filterText }
            });

            ODataSettings settings = query.ServiceProvider.GetRequiredService <ODataSettings>();

            FilterClause    filterClause     = queryOptionParser.ParseFilter();
            SingleValueNode filterExpression = filterClause.Expression.Accept(
                new ParameterAliasNodeTranslator(queryOptionParser.ParameterAliasNodes)) as SingleValueNode;

            filterExpression = filterExpression ?? new ConstantNode(null);
            filterClause     = new FilterClause(filterExpression, filterClause.RangeVariable);
            Contract.Assert(filterClause != null);

            var validator = new FilterQueryValidator(settings.DefaultQuerySettings);

            validator.Validate(filterClause, settings.ValidationSettings, edmModel);

            Expression filter = FilterBinder.Bind(query, filterClause, typeof(T), query.ServiceProvider);
            var        result = ExpressionHelpers.Where(query, filter, typeof(T));

            return(new ODataQuery <T>(result, query.ServiceProvider));
        }
        /// <summary>
        /// Apply $orderby parameter to query.
        /// </summary>
        /// <param name="query">
        /// The OData aware query.
        /// </param>
        /// <param name="orderbyText">
        /// The $orderby parameter text.
        /// </param>
        /// <param name="entitySetName">
        /// The entity set name.
        /// </param>
        /// <typeparam name="T">
        /// The query type param
        /// </typeparam>
        /// <returns>
        /// The <see cref="ODataQuery{T}"/> query with applied order by parameter.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Argument Null Exception
        /// </exception>
        public static ODataQueryOrdered <T> OrderBy <T>(this ODataQuery <T> query, string orderbyText, string entitySetName = null)
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }
            if (orderbyText == null)
            {
                throw new ArgumentNullException(nameof(orderbyText));
            }

            IEdmModel edmModel = query.EdmModel;

            ODataQueryOptionParser queryOptionParser = GetParser(
                query,
                entitySetName,
                new Dictionary <string, string> {
                { "$orderby", orderbyText }
            });

            ODataSettings settings = query.ServiceProvider.GetRequiredService <ODataSettings>();

            var orderByClause = queryOptionParser.ParseOrderBy();

            orderByClause = TranslateParameterAlias(orderByClause, queryOptionParser);

            ICollection <OrderByNode> nodes = OrderByNode.CreateCollection(orderByClause);

            var validator = new OrderByQueryValidator(settings.DefaultQuerySettings);

            validator.Validate(nodes, settings.ValidationSettings, edmModel);

            IOrderedQueryable <T> result = (IOrderedQueryable <T>)OrderByBinder.OrderApplyToCore(query, settings.QuerySettings, nodes, edmModel);

            return(new ODataQueryOrdered <T>(result, query.ServiceProvider));
        }