public Query(Type elementType, string query, ModelMetadataProvider provider) { if (elementType == null) throw new ArgumentNullException("elementType"); if (provider == null) throw new ArgumentNullException("provider"); _elementType = elementType; _provider = provider; if (string.IsNullOrWhiteSpace(query)) query = string.Empty; _originalQuery = query; var grammar = new QueryLanguageGrammar(); var language = new LanguageData(grammar); var parser = new Parser(language); var tree = parser.Parse(query); if (tree.Status == ParseTreeStatus.Parsed) { AssertEmpty(tree.Root.ChildNodes[2]); //groupBy AssertEmpty(tree.Root.ChildNodes[3]); //pivot AssertEmpty(tree.Root.ChildNodes[9]); //options _limit = ProcessIntNode(tree.Root.ChildNodes[5]); _offset = ProcessIntNode(tree.Root.ChildNodes[6]); //TODO: when handling pivot, need to rework this var metadata = provider.GetMetadataForType((Func<object>)null, elementType); var properties = metadata.Properties.ToList(); if (tree.Root.ChildNodes[0].ChildNodes.Count == 0 || (tree.Root.ChildNodes[0].ChildNodes.Count == 2 && tree.Root.ChildNodes[0].ChildNodes[1].Token != null && tree.Root.ChildNodes[0].ChildNodes[1].Token.Value == "*")) //select * { //select of all columns of elementType, just a pass through of all the columns _outputType = elementType; _selectStar = true; _columns = MetadataToColumns(properties.OrderBy(p => p.Order)); } else { _selectStar = false; //create custom output type unless select contains all columns just reordered... var selectList = tree.Root.ChildNodes[0].ClauseList(); if (!selectList.Any(e => !string.Equals(e.Term.Name, "identifier", StringComparison.Ordinal))) { //all identifiers, so just pass through current type for simplicity, no projection, exclude columns on client side _outputType = elementType; var selectListJoin = selectList.Join(properties, i => i.ToPropertyName(), p => p.PropertyName, (i,p) => p).ToList(); if (selectListJoin.Count != selectList.Count) throw new QueryParseException("Some of the identifiers passed in the select clause do not exist."); _columns = MetadataToColumns(selectListJoin); } else { throw new NotImplementedException(); } //populate columns list } var labelList = tree.Root.ChildNodes[7].ClauseList(); var labelListJoin = from l in labelList join c in _columns on l.ChildNodes[0].ToPropertyName() equals c.Name select new { l, c }; foreach (var j in labelListJoin) { j.c.Label = (string)j.l.ChildNodes[1].Token.Value; } var formatList = tree.Root.ChildNodes[8].ClauseList(); var formatListJoin = from f in formatList join c in _columns on f.ChildNodes[0].ToPropertyName() equals c.Name select new { f, c }; foreach (var j in formatListJoin) { j.c.Label = (string)j.f.ChildNodes[1].Token.Value; } //TODO: handle where AssertEmpty(tree.Root.ChildNodes[1]); //where var orderByList = tree.Root.ChildNodes[4].ClauseList(); var orderByListJoin = from o in orderByList join p in properties on o.ChildNodes[0].ToPropertyName() equals p.PropertyName into propertyGroup from p in propertyGroup.DefaultIfEmpty() select new { o, p }; foreach (var o in orderByListJoin) { if (o.p == null) throw new NotImplementedException("Can only order by identifiers."); var ascending = true; if (o.o.ChildNodes.Count == 2) { ascending = !string.Equals((string)o.o.ChildNodes[1].Token.Value, "desc", StringComparison.OrdinalIgnoreCase); } if (_orderBy == null) { if (ascending) { _orderBy = data => data.OrderBy(o.p.PropertyName); } else { _orderBy = data => data.OrderByDescending(o.p.PropertyName); } } else { if (ascending) { _orderBy = data => _orderBy(data.OrderBy(o.p.PropertyName)); } else { _orderBy = data => _orderBy(data.OrderByDescending(o.p.PropertyName)); } } } } else { throw new QueryParseException("Query parsing resulted in an error."); } }