public IParsedExpression Parse(string expression) { var tokens = expression.Split(".", StringSplitOptions.RemoveEmptyEntries); var parsedTokens = new List <QueryableToken>(); for (var i = 0; i < tokens.Length; ++i) { var token = tokens[i]; var isArrayExpression = token.EndsWith(']'); if (isArrayExpression) { var indexerStartPos = token.IndexOf('['); var indexer = token.Substring(indexerStartPos + 1, token.Length - indexerStartPos - 2); if (!int.TryParse(indexer, out var index)) { throw new ArgumentException($"Invalid indexer in array expression: {token}"); } var exprWithoutIndices = token.Substring(0, indexerStartPos); var p = new QueryableToken(string.Join('.', exprWithoutIndices), true, index); parsedTokens.Add(p); } else { var p = new QueryableToken(token, false, -1); parsedTokens.Add(p); } } var parsedExpr = new ReflectiveInvocationParsedExpression(parsedTokens.ToArray()); return(parsedExpr); }
/// <summary> /// Parses an expression for processing using a "direct invocation" strategy; the entire expression is broken up /// into sub-queries in order to determine if they are direct property calls or array index accesses. Queries that /// are all-properties will yield a single QueryableToken with the full call chain. For expressions that include array /// accessors, this will yield multiple QueryableTokens for each sub-expression. Each token will include the array index /// that was specified for that sub-expression. /// /// Supported expression formats include (the root object is implied here): /// "age" (two token property call chain) /// "property1.property2" (three token property call chain) /// "property1.arrayProperty2[INDEX0] (three token property with array indexer call chain) /// "arrayProperty1[INDEX0].arrayProperty2[INDEX1].Id (nested array indexer call chain) /// "arrayProperty1[INDEX0] (any array indexer IF the array is a primitive data type) /// </summary> /// <param name="expression">Expression to parse</param> /// <returns>ParsedQuery with parsing information</returns> public IParsedExpression Parse(string expression) { var tokens = expression.Split(".", StringSplitOptions.RemoveEmptyEntries); var parsedTokens = new List <QueryableToken>(); var subExprBuffer = new List <string>(tokens.Length); for (var i = 0; i < tokens.Length; ++i) { var token = tokens[i]; var isArrayExpression = token.EndsWith(']'); if (isArrayExpression) { var indexerStartPos = token.IndexOf('['); var indexer = token.Substring(indexerStartPos + 1, token.Length - indexerStartPos - 2); if (!int.TryParse(indexer, out var index)) { throw new ArgumentException($"Invalid indexer in array expression: {token}"); } var subExprWithoutIndexValue = token.Substring(0, indexerStartPos); subExprBuffer.Add(subExprWithoutIndexValue); // We strip out the index value because this will be our DAL lookup key var p = new QueryableToken(string.Join('.', subExprBuffer), true, index); parsedTokens.Add(p); subExprBuffer.Clear(); } else { subExprBuffer.Add(token); } } // Add final aggregate subexpr if anything is left. It won't be an array in this case since the array-check // within the loop would've already cleared it out if it was. if (subExprBuffer.Count > 0) { var aggPt = new QueryableToken(string.Join('.', subExprBuffer), false, -1); parsedTokens.Add(aggPt); } var result = new DirectInvocationParsedExpression(parsedTokens.ToArray()); return(result); }