public IRequest Parse <T>(string queryString)
            where T : class, new()
        {
            var requestUri = new Uri($"{nameof(T)}/{queryString}", UriKind.Relative);

            var parser = new ODataUriParser(GetEdmModel <T>(nameof(T)), requestUri)
            {
                Resolver = new StringAsEnumResolver()
                {
                    EnableCaseInsensitive = _uriParserSettings.EnableCaseInsensitive,
                },
            };

            bool?count = parser.ParseCount();
            long?top   = parser.ParseTop();
            long?skip  = parser.ParseSkip();

            var filter = parser.ParseFilter().Parse();

            return(new Request
            {
                Filter = filter,
                Select = parser.ParseSelectAndExpand().Parse(),
                Sorts = parser.ParseOrderBy().Parse(),
                Count = count.HasValue ? Convert.ToBoolean(count.Value) : false,
                Page = skip.HasValue ? Convert.ToInt32(skip.Value) + 1 : 1,
                PageSize = top.HasValue ? Convert.ToInt32(top.Value) : null
            });
        }
Beispiel #2
0
        private void ParseUriAndVerify(
            Uri uri,
            Action <ODataPath, FilterClause, OrderByClause, SelectExpandClause, IDictionary <string, SingleValueNode> > verifyAction)
        {
            // run 2 test passes:
            // 1. low level api - ODataUriParser instance methods
            {
                List <CustomQueryOptionToken> queries = UriUtils.ParseQueryOptions(uri);
                ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://gobbledygook/"), uri);

                ODataPath            path         = parser.ParsePath();
                IEdmNavigationSource entitySource = ResolveEntitySource(path);
                IEdmEntitySet        entitySet    = entitySource as IEdmEntitySet;

                var dic = queries.ToDictionary(customQueryOptionToken => customQueryOptionToken.Name, customQueryOptionToken => queries.GetQueryOptionValue(customQueryOptionToken.Name));
                ODataQueryOptionParser queryOptionParser = new ODataQueryOptionParser(HardCodedTestModel.TestModel, entitySet.EntityType(), entitySet, dic)
                {
                    Configuration = { ParameterAliasValueAccessor = parser.ParameterAliasValueAccessor }
                };

                FilterClause       filterClause       = queryOptionParser.ParseFilter();
                SelectExpandClause selectExpandClause = queryOptionParser.ParseSelectAndExpand();
                OrderByClause      orderByClause      = queryOptionParser.ParseOrderBy();

                // Two parser should share same ParameterAliasNodes
                verifyAction(path, filterClause, orderByClause, selectExpandClause, parser.ParameterAliasNodes);
                verifyAction(path, filterClause, orderByClause, selectExpandClause, queryOptionParser.ParameterAliasNodes);
            }

            //2. high level api - ParseUri
            {
                ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://gobbledygook/"), uri);
                verifyAction(parser.ParsePath(), parser.ParseFilter(), parser.ParseOrderBy(), parser.ParseSelectAndExpand(), parser.ParameterAliasNodes);
            }
        }
Beispiel #3
0
        public void ParseNoDollarQueryOptionsShouldReturnNullIfNoDollarQueryOptionsIsNotEnabled()
        {
            var parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("People?filter=MyDog/Color eq 'Brown'&select=ID&expand=MyDog&orderby=ID&top=1&skip=2&count=true&search=FA&$unknown=&$unknownvalue&skiptoken=abc&deltatoken=def", UriKind.Relative));

            bool originalValue = parser.EnableNoDollarQueryOptions;

            try
            {
                // Ensure $-sign is required.
                parser.EnableNoDollarQueryOptions = false;

                parser.ParseFilter().Should().BeNull();
                parser.ParseSelectAndExpand().Should().BeNull();
                parser.ParseOrderBy().Should().BeNull();
                parser.ParseTop().Should().Be(null);
                parser.ParseSkip().Should().Be(null);
                parser.ParseCount().Should().Be(null);
                parser.ParseSearch().Should().BeNull();
                parser.ParseSkipToken().Should().BeNull();
                parser.ParseDeltaToken().Should().BeNull();
            }
            finally
            {
                // Restore original value
                parser.EnableNoDollarQueryOptions = originalValue;
            }
        }
Beispiel #4
0
        public void EmptyValueQueryOptionShouldWork()
        {
            var uriParser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, new Uri(FullUri, "?$filter=&$select=&$expand=&$orderby=&$top=&$skip=&$count=&$search=&$unknow=&$unknowvalue&$skiptoken=&$deltatoken="));
            var path      = uriParser.ParsePath();

            path.Should().HaveCount(1);
            path.LastSegment.ShouldBeEntitySetSegment(HardCodedTestModel.GetPeopleSet());
            uriParser.ParseFilter().Should().BeNull();
            var results = uriParser.ParseSelectAndExpand();

            results.AllSelected.Should().BeTrue();
            results.SelectedItems.Should().HaveCount(0);
            uriParser.ParseOrderBy().Should().BeNull();
            Action action = () => uriParser.ParseTop();

            action.ShouldThrow <ODataException>().WithMessage(Strings.SyntacticTree_InvalidTopQueryOptionValue(""));
            action = () => uriParser.ParseSkip();
            action.ShouldThrow <ODataException>().WithMessage(Strings.SyntacticTree_InvalidSkipQueryOptionValue(""));
            action = () => uriParser.ParseCount();
            action.ShouldThrow <ODataException>().WithMessage(Strings.ODataUriParser_InvalidCount(""));
            action = () => uriParser.ParseSearch();
            action.ShouldThrow <ODataException>().WithMessage(Strings.UriQueryExpressionParser_ExpressionExpected(0, ""));
            uriParser.ParseSkipToken().Should().BeEmpty();
            uriParser.ParseDeltaToken().Should().BeEmpty();
        }
Beispiel #5
0
        public static SortDefinition <T> BuildSort <T>(this ODataUriParser query, ConvertProperty propertyCalculator = null)
        {
            var orderBy = query.ParseOrderBy();

            if (orderBy != null)
            {
                var sorts = new List <SortDefinition <T> >();

                while (orderBy != null)
                {
                    sorts.Add(OrderBy <T>(orderBy, propertyCalculator));

                    orderBy = orderBy.ThenBy;
                }

                if (sorts.Count > 1)
                {
                    return(Builders <T> .Sort.Combine(sorts));
                }
                else
                {
                    return(sorts[0]);
                }
            }

            return(null);
        }
Beispiel #6
0
        public static SortDefinition <MongoContentEntity> BuildSort(ODataUriParser query, Schema schema)
        {
            var orderBy = query.ParseOrderBy();

            if (orderBy != null)
            {
                var sorts = new List <SortDefinition <MongoContentEntity> >();

                while (orderBy != null)
                {
                    sorts.Add(OrderBy(orderBy, schema));

                    orderBy = orderBy.ThenBy;
                }

                if (sorts.Count > 1)
                {
                    return(Sort.Combine(sorts));
                }
                else
                {
                    return(sorts[0]);
                }
            }
            else
            {
                return(Sort.Descending(x => x.LastModified));
            }
        }
        public void CreateCollection_From_OrderByNode_Succeeds()
        {
            // Arrange
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

            builder.EntitySet <SampleClass>("entityset");

            IEdmModel      model = builder.GetEdmModel();
            IEdmEntityType sampleClassEntityType = model.SchemaElements.Single(t => t.Name == "SampleClass") as IEdmEntityType;
            OrderByClause  orderbyNode           = ODataUriParser.ParseOrderBy("Property1 desc, Property2 asc", model, sampleClassEntityType);

            // Act
            ICollection <OrderByNode> nodes = OrderByNode.CreateCollection(orderbyNode);

            // Assert
            Assert.False(nodes.OfType <OrderByItNode>().Any());
            IEnumerable <OrderByPropertyNode> propertyNodes = nodes.OfType <OrderByPropertyNode>();

            Assert.Equal(2, propertyNodes.Count());
            Assert.Equal("Property1", propertyNodes.First().Property.Name);
            Assert.Equal(OrderByDirection.Descending, propertyNodes.First().Direction);

            Assert.ReferenceEquals("Property2", propertyNodes.Last().Property.Name);
            Assert.Equal(OrderByDirection.Ascending, nodes.Last().Direction);
        }
        public void EmptyValueQueryOptionShouldWork(string relativeUriString, bool enableNoDollarQueryOptions)
        {
            var uriParser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, new Uri(FullUri, relativeUriString));

            uriParser.EnableNoDollarQueryOptions = enableNoDollarQueryOptions;
            var path = uriParser.ParsePath();

            path.Should().HaveCount(1);
            path.LastSegment.ShouldBeEntitySetSegment(HardCodedTestModel.GetPeopleSet());
            uriParser.ParseFilter().Should().BeNull();
            var results = uriParser.ParseSelectAndExpand();

            results.AllSelected.Should().BeTrue();
            results.SelectedItems.Should().HaveCount(0);
            uriParser.ParseOrderBy().Should().BeNull();
            Action action = () => uriParser.ParseTop();

            action.ShouldThrow <ODataException>().WithMessage(Strings.SyntacticTree_InvalidTopQueryOptionValue(""));
            action = () => uriParser.ParseSkip();
            action.ShouldThrow <ODataException>().WithMessage(Strings.SyntacticTree_InvalidSkipQueryOptionValue(""));
            action = () => uriParser.ParseCount();
            action.ShouldThrow <ODataException>().WithMessage(Strings.ODataUriParser_InvalidCount(""));
            action = () => uriParser.ParseSearch();
            action.ShouldThrow <ODataException>().WithMessage(Strings.UriQueryExpressionParser_ExpressionExpected(0, ""));
            uriParser.ParseSkipToken().Should().BeEmpty();
            uriParser.ParseDeltaToken().Should().BeEmpty();
        }
Beispiel #9
0
        protected void ApprovalVerifyOrderByParser(Uri resourceRoot, string queryOption, IEdmModel model = null)
        {
            ODataUriParser parser = this.CreateOrderByUriParser(resourceRoot, queryOption, model);
            var            result = parser.ParseOrderBy();

            ApprovalVerify(QueryNodeToStringVisitor.GetTestCaseAndResultString(result, queryOption));
        }
Beispiel #10
0
        public void ShouldBeNullOrderByOperatorInstance()
        {
            ODataUriParser parser = GetParser("/Enterprises");

            ISqlBinder binder = new SqlOrderByBinder(parser.ParseOrderBy());

            binder.Query.Should().BeNull();
        }
Beispiel #11
0
        public void ShouldBeOrderByOperatorResolverInstance()
        {
            ODataUriParser parser = GetParser("/Enterprises?$orderby=EnterpriseName");

            ISqlBinder binder = new SqlOrderByBinder(parser.ParseOrderBy());

            binder.Query.Should().BeOfType <OrderByOperatorResolver>();
        }
Beispiel #12
0
        public void ShouldBeOrderByOperatorForTwoFields()
        {
            ODataUriParser parser = GetParser("/Enterprises?$orderby=EnterpriseName,PartnerName%20desc");

            IQueryResolver resolver = new OrderByOperatorResolver(parser.ParseOrderBy());

            resolver.Resolve().Should().Be("ORDER BY EnterpriseName asc, PartnerName desc");
        }
Beispiel #13
0
        public void OrderByLimitWithInterestingTreeStructures()
        {
            ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, new Uri("http://host/People?$orderby=MyDog/MyPeople/MyDog/MyPeople/MyPaintings asc"))
            {
                Settings = { OrderByLimit = 5 }
            };
            Action parseWithLimit = () => parser.ParseOrderBy();

            parseWithLimit.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.UriQueryExpressionParser_TooDeep);
        }
Beispiel #14
0
        public void OrderByLimitIsRespectedForOrderby()
        {
            ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, new Uri("http://host/People?$orderby= 1 eq 1"))
            {
                Settings = { OrderByLimit = 0 }
            };
            Action parseWithLimit = () => parser.ParseOrderBy();

            parseWithLimit.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.UriQueryExpressionParser_TooDeep);
        }
 /// <summary>Parses the text expression for ordering.</summary>
 /// <returns>An orderby clause.</returns>
 internal OrderByClause ParseOrderBy()
 {
     try
     {
         return(odataUriParser.ParseOrderBy());
     }
     catch (ODataException ex)
     {
         throw new DataServiceException(400, null, ex.Message, null, ex);
     }
 }
        public void ParseQueryOptionsShouldWork()
        {
            var parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("People?$filter=MyDog/Color eq 'Brown'&$select=ID&$expand=MyDog&$orderby=ID&$top=1&$skip=2&$count=true&$search=FA&$unknow=&$unknowvalue", UriKind.Relative));

            parser.ParseSelectAndExpand().Should().NotBeNull();
            parser.ParseFilter().Should().NotBeNull();
            parser.ParseOrderBy().Should().NotBeNull();
            parser.ParseTop().Should().Be(1);
            parser.ParseSkip().Should().Be(2);
            parser.ParseCount().Should().Be(true);
            parser.ParseSearch().Should().NotBeNull();
        }
        public void OrderByLimitWithInterestingTreeStructures(string fullUriString, bool enableNoDollarQueryOptions)
        {
            ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, new Uri(fullUriString))
            {
                Settings = { OrderByLimit = 5 }
            };

            parser.EnableNoDollarQueryOptions = enableNoDollarQueryOptions;
            Action parseWithLimit = () => parser.ParseOrderBy();

            parseWithLimit.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.UriQueryExpressionParser_TooDeep);
        }
Beispiel #18
0
        public void CustomPromotionRuleTest()
        {
            Uri            query  = new Uri("People?$orderby=FavoriteDate add 3", UriKind.Relative);
            ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, query)
            {
                Resolver = new MiscPromotionResolver()
            };
            var clause = parser.ParseOrderBy();
            var node   = clause.Expression.ShouldBeBinaryOperatorNode(BinaryOperatorKind.Add).And;

            node.TypeReference.IsBinary().Should().BeTrue();
            node.Left.ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonFavoriteDateProp());
            node.Right.ShouldBeConstantQueryNode("3");
        }
Beispiel #19
0
        public static void ParseSort(this ODataUriParser query, ClrQuery result)
        {
            var orderBy = query.ParseOrderBy();

            if (orderBy != null)
            {
                while (orderBy != null)
                {
                    result.Sort.Add(OrderBy(orderBy));

                    orderBy = orderBy.ThenBy;
                }
            }
        }
Beispiel #20
0
        public void SpatialLengthNotLineString()
        {
            ODataUriParser parser = this.CreateOrderByUriParser(bossBase, "geo.length(geometry'Polygon((10 30, 7 28, 6 6, 10 30))') lt 0.5");

            try
            {
                parser.ParseOrderBy();
            }
            catch (ODataException e)
            {
                var expected = ODataExpectedExceptions.ODataException("MetadataBinder_NoApplicableFunctionFound", "geo.length", "geo.length(Edm.GeometryLineString Nullable=true); geo.length(Edm.GeographyLineString Nullable=true)");
                expected.ExpectedMessage.Verifier.VerifyMatch("MetadataBinder_NoApplicableFunctionFound", e.Message, "geo.length", "geo.length(Edm.GeometryLineString Nullable=true); geo.length(Edm.GeographyLineString Nullable=true)");
            }
        }
Beispiel #21
0
        public void SpatialLengthPolygonNotFormedCorrectly()
        {
            ODataUriParser parser = this.CreateOrderByUriParser(bossBase, "geo.length(geometry'Polygon(10 30, 7 28, 6 6, 10 30)') lt 0.5");

            try
            {
                parser.ParseOrderBy();
            }
            catch (ODataException e)
            {
                var expected = ODataExpectedExceptions.ODataException("UriQueryExpressionParser_UnrecognizedLiteralWithReason", "Edm.Geometry", "geometry'Polygon(10 30, 7 28, 6 6, 10 30)'", "11", "geo.length(geometry'Polygon(10 30, 7 28, 6 6, 10 30)') lt 0.5", "Expecting token type \"LeftParen\" with text \"\" but found \"Type:[2] Text:[10]\".");
                expected.ExpectedMessage.Verifier.VerifyMatch("UriQueryExpressionParser_UnrecognizedLiteralWithReason", e.Message, "Edm.Geometry", "geometry'Polygon(10 30, 7 28, 6 6, 10 30)'", "11", "geo.length(geometry'Polygon(10 30, 7 28, 6 6, 10 30)') lt 0.5", "Expecting token type \"LeftParen\" with text \"\" but found \"Type:[2] Text:[10]\".");
            }
        }
Beispiel #22
0
        private static void TestCaseInsensitive()
        {
            Console.WriteLine("TestCaseInsensitive");
            var parser = new ODataUriParser(
                extModel.Model,
                ServiceRoot,
                new Uri("http://demo/odata.svc/People(1)/Pets/TestNS.Fish?$orderby=Color"));

            var path   = parser.ParsePath();
            var clause = parser.ParseOrderBy();

            Console.WriteLine(path.ToLogString());
            Console.WriteLine(clause.Expression.ToLogString());

            var parser2 = new ODataUriParser(
                extModel.Model,
                ServiceRoot,
                new Uri("http://demo/odata.svc/people(1)/pets/testns.fish?$ORDERBY=color"))
            {
                Resolver = new ODataUriResolver {
                    EnableCaseInsensitive = true
                }
            };

            // Identical to path and clause
            var path2   = parser2.ParsePath();
            var clause2 = parser2.ParseOrderBy();

            Console.WriteLine(path2.ToLogString());
            Console.WriteLine(clause2.Expression.ToLogString());

            // Query option parser also supports custom resolver
            var parser3 = new ODataQueryOptionParser(
                extModel.Model,
                extModel.Fish,
                extModel.PetSet,
                new Dictionary <string, string>
            {
                { "$orderby", "color" }
            })
            {
                Resolver = new ODataUriResolver {
                    EnableCaseInsensitive = true
                }
            };
            var clause3 = parser3.ParseOrderBy();

            Console.WriteLine(clause3.Expression.ToLogString());
        }
        public void NoneQueryOptionShouldWork()
        {
            var uriParser = new ODataUriParser(HardCodedTestModel.TestModel, ServiceRoot, FullUri);
            var path      = uriParser.ParsePath();

            path.Should().HaveCount(1);
            path.LastSegment.ShouldBeEntitySetSegment(HardCodedTestModel.GetPeopleSet());
            uriParser.ParseFilter().Should().BeNull();
            uriParser.ParseSelectAndExpand().Should().BeNull();
            uriParser.ParseOrderBy().Should().BeNull();
            uriParser.ParseTop().Should().Be(null);
            uriParser.ParseSkip().Should().Be(null);
            uriParser.ParseCount().Should().Be(null);
            uriParser.ParseSearch().Should().BeNull();
        }
        public void ParseQueryOptionsShouldWork(string relativeUriString, bool enableNoDollarQueryOptions)
        {
            var parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri(relativeUriString, UriKind.Relative));

            parser.EnableNoDollarQueryOptions = enableNoDollarQueryOptions;
            parser.ParseSelectAndExpand().Should().NotBeNull();
            parser.ParseFilter().Should().NotBeNull();
            parser.ParseOrderBy().Should().NotBeNull();
            parser.ParseTop().Should().Be(1);
            parser.ParseSkip().Should().Be(2);
            parser.ParseCount().Should().Be(true);
            parser.ParseSearch().Should().NotBeNull();
            parser.ParseSkipToken().Should().Be("abc");
            parser.ParseDeltaToken().Should().Be("def");
        }
        public void CreateCollection_CopmplexType_Succeeds()
        {
            // Arrange
            CustomersModelWithInheritance model = new CustomersModelWithInheritance();
            OrderByClause orderByNode           = ODataUriParser.ParseOrderBy("Street desc, City asc", model.Model, model.Address);

            // Act
            ICollection <OrderByNode> nodes = OrderByNode.CreateCollection(orderByNode);

            // Assert
            Assert.Equal(2, nodes.Count());
            Assert.Equal("Street", (nodes.ToList()[0] as OrderByPropertyNode).Property.Name);
            Assert.Equal(OrderByDirection.Descending, nodes.ToList()[0].Direction);
            Assert.Equal("City", (nodes.ToList()[1] as OrderByPropertyNode).Property.Name);
            Assert.Equal(OrderByDirection.Ascending, nodes.ToList()[1].Direction);
        }
        public void CaseInsensitiveTypeCastInQueryOptionOrderBy2()
        {
            var uriParser = new ODataUriParser(
                Model,
                ServiceRoot,
                new Uri("http://host/People?$orderby=cast(null, Edm.StRing)"))
            {
                Resolver = new ODataUriResolver()
                {
                    EnableCaseInsensitive = true
                }
            };

            var clause = uriParser.ParseOrderBy();

            clause.Expression.ShouldBeSingleValueFunctionCallQueryNode("cast", EdmCoreModel.Instance.GetString(false));
        }
Beispiel #27
0
        /// <summary>
        /// Esse Controller está implementando a lógica para converter parametros OData para parametros de StoredProcedure.
        /// Não dá pra usar [EnableQuery], senão os parametros da query serão aplicados na SP e posteriormente no retorno dela.
        /// </summary>
        /// <param name="queryOptions"></param>
        /// <returns></returns>
        // GET: odata/Enterprises
        public IQueryable <EnterprisesModel> GetEnterprises(ODataQueryOptions queryOptions)
        {
            //TODO: O código abaixo deve ser posteriormente movido para um Filter do Controller,
            // por ser genérico irá se repetir em cada GET do Controller.
            Uri            fullUri = new Uri(ControllerContext.Request.RequestUri.PathAndQuery.Replace("odata/", ""), UriKind.Relative);
            ODataUriParser parser  = new ODataUriParser(_model, fullUri);                //Poderia ser via ODataQueryOptionParser, mas achei mais complicado

            FilterClause  filterClause  = parser.ParseFilter();                          // parse $filter
            OrderByClause orderByClause = parser.ParseOrderBy();                         // parse $orderby
            long?         topClause     = parser.ParseTop();                             // parse $top
            long?         skipClause    = parser.ParseSkip();                            // parse $skip
            bool          count         = parser.ParseCount() ?? false;                  // parse $count

            SqlFilterBinder  filterBinder  = new SqlFilterBinder(filterClause);          //Binder para resolver o filtro; TODO: Melhorar para componente e UNIT TESTE
            SqlOrderByBinder orderByBinder = new SqlOrderByBinder(orderByClause);        //Binder para resolver o ordenação; TODO: Melhorar para componente e UNIT TESTE
            SqlPagingBinder  pagingBinder  = new SqlPagingBinder(topClause, skipClause); //Binder para resolver o paginação; TODO: Melhorar para componente e UNIT TESTE

            //Agora é só aplicar os parametros encontrados na Stored Procedure
            return(db.ProcPgdEnterprisesRetrieveAllMonitoredFromUserPartner(7, filterBinder.Resolve(), orderByBinder.Resolve(), pagingBinder.Resolve(), count).AsQueryable());
        }
Beispiel #28
0
        public void ParseWithAllQueryOptionsWithoutAlias()
        {
            ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://www.odata.com/OData/"), new Uri("http://www.odata.com/OData/Dogs?$select=Color, MyPeople&$expand=MyPeople&$filter=startswith(Color, 'Blue')&$orderby=Color asc"));

            parser.ParsePath().FirstSegment.ShouldBeEntitySetSegment(HardCodedTestModel.GetDogsSet());
            var myDogSelectedItems = parser.ParseSelectAndExpand().SelectedItems.ToList();

            myDogSelectedItems.Count.Should().Be(3);
            myDogSelectedItems[1].ShouldBePathSelectionItem(new ODataPath(new PropertySegment(HardCodedTestModel.GetDogColorProp())));
            var myPeopleExpansionSelectionItem = myDogSelectedItems[0].ShouldBeSelectedItemOfType <ExpandedNavigationSelectItem>().And;

            myPeopleExpansionSelectionItem.PathToNavigationProperty.Single().ShouldBeNavigationPropertySegment(HardCodedTestModel.GetDogMyPeopleNavProp());
            myPeopleExpansionSelectionItem.SelectAndExpand.SelectedItems.Should().BeEmpty();
            var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode("startswith").And.Parameters.ToList();

            startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetDogColorProp());
            startsWithArgs[1].ShouldBeConstantQueryNode("Blue");
            var orderby = parser.ParseOrderBy();

            orderby.Direction.Should().Be(OrderByDirection.Ascending);
            orderby.Expression.ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetDogColorProp());
        }
        public ParseContext Parse(ODataUriParser parser, ParseContext parseContext)
        {
            var sourceEdmSetting = parseContext.EdmEntityTypeSettings.FirstOrDefault();

            // Get primary collection type and setup collection
            var collectionEntityTypeKey = parseContext.LatestStateDictionary
                                          .Keys.FirstOrDefault(p => p.Contains("collectionentitytype"));
            var entityRef     = (EdmEntityTypeReference)parseContext.LatestStateDictionary[collectionEntityTypeKey];
            var collectionRef = new EdmCollectionTypeReference(new EdmCollectionType(entityRef));
            var collection    = new EdmEntityObjectCollection(collectionRef);

            // Get orderby clause
            var orderByClause = parser.ParseOrderBy();

            bool isFirstIter = true;
            IEnumerable <IEdmEntityObject>                    resultStart    = parseContext.Result;
            IEnumerable <Dictionary <string, object> >        queryableStart = parseContext.QueryableSourceEntities;
            IOrderedEnumerable <IEdmEntityObject>             result         = null;
            IOrderedEnumerable <Dictionary <string, object> > queryable      = null;

            while (orderByClause != null)
            {
                // Get attribute name
                string attributeName = "";
                var    kind          = orderByClause.Expression.Kind;
                if (kind == QueryNodeKind.SingleValueOpenPropertyAccess)
                {
                    attributeName = ((SingleValueOpenPropertyAccessNode)orderByClause.Expression).Name;
                }
                else if (kind == QueryNodeKind.SingleValuePropertyAccess)
                {
                    attributeName = ((SingleValuePropertyAccessNode)orderByClause.Expression).Property.Name;
                }
                else
                {
                    throw new FeatureNotSupportedException(OrderByParser, $"QueryNodeKind: {kind} Not Supported");
                }
                // Check if attribute name in model
                var typeSetting = sourceEdmSetting.Properties.FirstOrDefault(predicate => predicate.PropertyName == attributeName);
                // Get direction
                var direction = orderByClause.Direction.ToString();
                // Perform ordering
                Func <IEdmEntityObject, object>            resultExpression    = item => GetPropertyValue(item, attributeName);
                Func <Dictionary <string, object>, object> queryableExpression = item => item[attributeName];
                if (string.Compare(direction, "Ascending") == 0)
                {
                    result = (isFirstIter) ? resultStart.OrderBy(resultExpression) :
                             result.ThenBy(resultExpression);
                    queryable = (isFirstIter) ? queryableStart.OrderBy(queryableExpression) :
                                queryable.ThenBy(queryableExpression);
                }
                else
                {
                    result = (isFirstIter) ? resultStart.OrderByDescending(resultExpression) :
                             result.ThenByDescending(resultExpression);
                    queryable = (isFirstIter) ? queryableStart.OrderByDescending(queryableExpression) :
                                queryable.ThenByDescending(queryableExpression);
                }
                isFirstIter = false;
                // Go to next ordering clause
                orderByClause = orderByClause.ThenBy;
            }
            // Create collection
            foreach (var entity in result)
            {
                collection.Add(entity);
            }

            var targetParseContext = new ParseContext
            {
                Result = collection,
                QueryableSourceEntities = queryable,
                Model = parseContext.Model,
                EdmEntityTypeSettings = parseContext.EdmEntityTypeSettings,
                LatestStateDictionary = parseContext.LatestStateDictionary
            };

            return(targetParseContext);
        }