public void LambdaExpression()
        {
            var tparameter = ODataExpression.Parameter("t");
            var cparameter = ODataExpression.Parameter("c");
            var l1         = new ODataLambdaExpression
            {
                Body       = ODataExpression.PropertyOrField("Items", cparameter),
                Parameters = new[] { cparameter }
            };

            var aparameter = ODataExpression.Parameter("a");
            var bparameter = ODataExpression.Parameter("b");
            var l2         = new ODataLambdaExpression
            {
                Body       = ODataExpression.Add(ODataExpression.PropertyOrField("Id", aparameter), ODataExpression.PropertyOrField("Id", bparameter)),
                Parameters = new[] { aparameter, bparameter }
            };

            var method = new ODataMethodCallExpression
            {
                Context    = ODataExpression.PropertyOrField("Customers", tparameter),
                MethodName = "SelectMany",
                Arguments  = new ODataExpression[]
                {
                    l1, l2
                }
            };

            var expression = new ODataLambdaExpression
            {
                Body       = method,
                Parameters = new [] { tparameter }
            };
            //t => t.Customers.SelectMany(c => c.Customers, (a, b) => (a.Id + b.Id))
            //t => t.Customers.SelectMany(c => c.Customers, (a, b) => (a.Id + b.Id))


            var methods =
                typeof(Queryable).GetMethods()
                .Where(x => x.Name == "SelectMany" && x.GetParameters().Length == 3)
                .ToArray();

            foreach (var methodInfo in methods)
            {
                //   var arguments = methodInfo.GetGenericArguments();

                // var parameter = DynamicExpression.Parameter(arguments[0], "t");

                //   var dd = DynamicExpression.Property(parameter, "Customers");
                //
            }


            //var expr = Linq.Linq.Expr<Customer, IEnumerable<int>>(t => t.Customers.SelectMany(c => c.Items, (a, b) => a.Id + b.Id));
            //var translator = new QueryTranslator();

            //var d = translator.Invoke<Customer, IEnumerable<int>>(expression);
        }
        private ODataExpression ParseIdentifier(bool minus, ODataParameterExpression parameter, ODataExpression parent = null, IDictionary <string, ODataParameterExpression> lambdaParameters = null)
        {
            for (_current++; _current < _source.Length; _current++)
            {
                var c = _source[_current];

                if (!IsIdentifierChar(c))
                {
                    break;
                }
            }

            var name = _source.Substring(_offset, _current - _offset);

            var lastOffset = _offset;

            _offset = _current;

            switch (name)
            {
            case "INF":
                return(ODataExpression.Constant(double.PositiveInfinity));

            case "-INF":
                return(ODataExpression.Constant(double.NegativeInfinity));

            case "Nan":
                return(ODataExpression.Constant(double.NaN));

            case "true":
                return(ODataExpression.Constant(true));

            case "false":
                return(ODataExpression.Constant(false));

            case "null":
                return(ODataExpression.Constant(null, typeof(object)));

            case "-":
            {
                return(new ODataUnaryExpression(ExpressionType.Negate));
            }

            default:
                if (minus)
                {
                    // Reset the offset.
                    _offset = lastOffset + 1;

                    return(new ODataUnaryExpression(ExpressionType.Negate));
                }
                break;
            }

            if (_offset < _source.Length)
            {
                switch (_source[_offset])
                {
                case '\'':
                {
                    StringType stringType;

                    switch (name)
                    {
                    case "X": stringType = StringType.Binary; break;

                    case "binary": stringType = StringType.Binary; break;

                    case "datetime": stringType = StringType.DateTime; break;

                    case "guid": stringType = StringType.Guid; break;

                    case "time": stringType = StringType.Time; break;

                    case "datetimeoffset": stringType = StringType.DateTimeOffset; break;

                    default: stringType = StringType.None; break;
                    }

                    if (stringType != StringType.None && _source[_offset] == '\'')
                    {
                        var content = ParseString();

                        return(ParseSpecialString((string)content.Value, stringType));
                    }

                    if (stringType == StringType.None)
                    {
                        var content = ParseString();

                        return(ParceValue(name, (string)content.Value));
                    }

                    break;
                }

                case ':':
                {
                    _offset++;

                    var depth = 0;

                    var p = ODataExpression.Parameter(name);

                    var lp = new Dictionary <string, ODataParameterExpression>(lambdaParameters ?? new Dictionary <string, ODataParameterExpression>());

                    lp[p.Name] = p;

                    var tokens = new List <ODataExpression>();

                    while (true)
                    {
                        var token = GetNext(parameter, null, lp);

                        if (token == null)
                        {
                            break;
                        }

                        if (token.NodeType == ExpressionType.Default)
                        {
                            var syntaxExpressionToken = (ODataSyntaxExpression)token;

                            if (syntaxExpressionToken.Syntax == ',')
                            {
                                _offset--;
                                break;
                            }

                            if (syntaxExpressionToken.Syntax == '(')
                            {
                                depth++;
                            }

                            if (syntaxExpressionToken.Syntax == ')')
                            {
                                if (depth == 0)
                                {
                                    _offset--;
                                    break;
                                }

                                depth--;
                            }
                        }

                        tokens.Add(token);
                    }

                    var body = CreateExpression(ConvertToRpn(tokens));

                    var lambdaExpression = new ODataLambdaExpression
                    {
                        Parameters = new[] { p },
                        Body       = body
                    };

                    return(lambdaExpression);
                }

                case '/':
                {
                    _offset++;

                    if (lambdaParameters != null && lambdaParameters.ContainsKey(name) && parent == null)
                    {
                        return(ParseIdentifier(false, parameter, lambdaParameters[name], lambdaParameters));
                    }
                    if (name.StartsWith("Ase.") && parent != null && name.Substring(4) == parent.ToString().Replace(parameter.ToString() + ".", ""))
                    {
                        return(ParseIdentifier(false, parameter, parent, lambdaParameters));
                    }

                    return(ParseIdentifier(false, parameter, ODataExpression.PropertyOrField(name, parent ?? parameter), lambdaParameters));
                }

                case '(':    //Если следующий элемент скобка, значит это функция
                {
                    var depth     = 0;
                    var comma     = false;
                    var arguments = new List <ODataExpression>();
                    var temp      = new List <ODataExpression>();

                    while (true)
                    {
                        var token = GetNext(parameter, null, lambdaParameters);

                        if (token == null)
                        {
                            break;
                        }
                        var syntax = token as ODataSyntaxExpression;

                        if (syntax != null && syntax.Syntax == ',')
                        {
                            if (temp.Any())
                            {
                                var tokens = ConvertToRpn(temp.ToArray());

                                var expression = CreateExpression(tokens);

                                arguments.Add(expression);

                                temp = new List <ODataExpression>();

                                comma = true;
                            }
                            else
                            {
                                throw new Exception("extra comma");
                            }
                        }
                        else
                        {
                            if (syntax != null && syntax.Syntax == '(')
                            {
                                if (comma)
                                {
                                    throw new Exception("extra comma");
                                }

                                depth++;
                            }

                            if (syntax != null && syntax.Syntax == ')')
                            {
                                if (comma)
                                {
                                    throw new Exception("extra comma");
                                }

                                depth--;
                            }

                            if (syntax == null ||
                                !(syntax.Syntax == '(' && depth == 1) && !(syntax.Syntax == ')' && depth == 0))
                            {
                                temp.Add(token);
                            }

                            comma = false;

                            if (depth == 0)
                            {
                                if (temp.Any())
                                {
                                    var tokens = ConvertToRpn(temp.ToArray());

                                    var expression = CreateExpression(tokens);


                                    arguments.Add(expression);
                                }

                                break;
                            }
                        }
                    }

                    if (depth != 0)
                    {
                        throw new Exception("Parenthesis mismatch");
                    }


                    var methodCallExpression = new ODataMethodCallExpression
                    {
                        Context    = parent,
                        MethodName = name,
                        Arguments  = arguments.ToArray()
                    };

                    if (_offset < _source.Length && _source[_offset] == '/')
                    {
                        _current++;

                        _offset = _offset + 1;


                        return(ParseIdentifier(false, parameter, methodCallExpression, lambdaParameters));
                    }


                    return(methodCallExpression);
                }
                }
            }

            if (name.IsOperator())
            {
                var expressionType = name.GetExpressionType();

                if (name.IsUnaryOperator())
                {
                    return(ODataExpression.MakeUnary(expressionType, null));
                }

                if (name.IsArithmeticOperator() || name.IsLogicalOperator())
                {
                    return(ODataExpression.MakeBinary(expressionType, null, null));
                }
            }

            if (parent == null && lambdaParameters != null && lambdaParameters.ContainsKey(name))
            {
                return(lambdaParameters[name]);
            }

            if (name.Contains("."))
            {
                var type = GetType(name);
                return(ODataExpression.Constant(type));
            }

            return(ODataExpression.PropertyOrField(name, parent ?? parameter));
        }
Exemple #3
0
        private Expression VisitMethodCall(ODataMethodCallExpression node)
        {
            var functionName = node.MethodName.ToLowerInvariant();

            switch (functionName)
            {
            case "contains":
            {
                var right = Visit(node.Arguments[0]);
                var left  = Visit(node.Arguments[1]);

                return(Expression.Call(right, MethodProvider.ContainsMethod, new[] { left }));
            }

            case "endswith":
            {
                var right = Visit(node.Arguments[1]);
                var left  = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.EndsWithMethod, new[] { right }));
            }

            case "startswith":
            {
                var right = Visit(node.Arguments[1]);
                var left  = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.StartsWithMethod, new[] { right }));
            }

            case "length":
            {
                var left = Visit(node.Arguments[0]);

                return(Expression.Property(left, MethodProvider.LengthProperty));
            }

            case "indexof":
            {
                var right = Visit(node.Arguments[1]);
                var left  = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.IndexOfMethod, new[] { right }));
            }

            case "substring":
            {
                if (node.Arguments.Length == 3)
                {
                    return(Expression.Call(Visit(node.Arguments[0]), MethodProvider.SubstringMethodWithTwoArg, new[] { Visit(node.Arguments[1]), Visit(node.Arguments[2]) }));
                }

                if (node.Arguments.Length == 2)
                {
                    return(Expression.Call(Visit(node.Arguments[0]), MethodProvider.SubstringMethodWithOneArg, new[] { Visit(node.Arguments[1]) }));
                }

                throw new NotSupportedException();        //Нет перегрузки для данного метода
            }

            case "tolower":
            {
                var left = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.ToLowerMethod));
            }

            case "toupper":
            {
                var left = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.ToUpperMethod));
            }

            case "trim":
            {
                var left = Visit(node.Arguments[0]);

                return(Expression.Call(left, MethodProvider.TrimMethod));
            }

            case "hour":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.HourProperty));
            }

            case "minute":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.MinuteProperty));
            }

            case "second":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.SecondProperty));
            }

            case "day":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.DayProperty));
            }

            case "month":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.MonthProperty));
            }

            case "year":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Property(left, MethodProvider.YearProperty));
            }

            case "round":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Call(left.Type == typeof(double) ? MethodProvider.DoubleRoundMethod : MethodProvider.DecimalRoundMethod, new[] { left }));
            }

            case "floor":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Call(left.Type == typeof(double) ? MethodProvider.DoubleFloorMethod : MethodProvider.DecimalFloorMethod, new[] { left }));
            }

            case "ceiling":
            {
                var left = Visit(node.Arguments[0]);

                if (left.Type.IsNullableType())
                {
                    left = Expression.Property(left, "Value");
                }

                return(Expression.Call(left.Type == typeof(double) ? MethodProvider.DoubleCeilingMethod : MethodProvider.DecimalCeilingMethod, new[] { left }));
            }

            case "concat":
            {
                var right = Visit(node.Arguments[1]);
                var left  = Visit(node.Arguments[0]);

                return(Expression.Add(left, right, MethodProvider.ConcatMethod));
            }

            case "replace":
            {
                return(Expression.Call(Visit(node.Arguments[0]), MethodProvider.ReplaceMethod, new[] { Visit(node.Arguments[1]), Visit(node.Arguments[2]) }));
            }

            case "sum":
            case "average":
            {
                var context = Visit(node.Context);

                var genericArguments = context.Type.GetTypeGenericArguments();

                var arguments = node.Arguments.Select(argument => argument.NodeType == ExpressionType.Lambda ? VisitLambda((ODataLambdaExpression)argument, genericArguments[0]) : Visit(argument)).ToList();

                arguments.Insert(0, context);

                var type = context.Type.IsIQueryable() ? typeof(Queryable) : typeof(Enumerable);

                return(Expression.Call(type, functionName, arguments.Count == 1 ? Type.EmptyTypes : genericArguments, arguments.ToArray()));
            }

            case "min":
            case "max":
            case "selectmany":
            {
                var context = Visit(node.Context);

                var genericArguments = context.Type.GetGenericArguments();

                var arguments = node.Arguments.Select(argument => argument.NodeType == ExpressionType.Lambda ? VisitLambda((ODataLambdaExpression)argument, genericArguments[0]) : Visit(argument)).ToList();

                arguments.Insert(0, context);

                var type = context.Type.IsIQueryable() ? typeof(Queryable) : typeof(Enumerable);

                var expr = ExpressionMethodBinder.Bind(type, functionName, null, arguments.ToArray());

                return(expr);
            }

            case "groupby":
            case "orderby":
            case "orderbydescending":
            case "thenby":
            case "thenbydescending":
            case "select":
            {
                var context = Visit(node.Context);

                var genericArguments = context.Type.GetTypeGenericArguments().ToList();

                var lambda = (LambdaExpression)VisitLambda((ODataLambdaExpression)node.Arguments[0], genericArguments[0]);

                genericArguments.Add(lambda.ReturnType);

                var type = context.Type.IsIQueryable() ? typeof(Queryable) : typeof(Enumerable);

                return(Expression.Call(type, functionName, genericArguments.ToArray(), new[] { context, lambda }));
            }

            case "any":
            case "all":
            case "first":
            case "where":
            case "take":
            case "skip":
            case "distinct":
            case "last":
            case "lastordefault":
            case "single":
            case "singleordefault":
            case "defaultifempty":
            case "count":
            case "longcount":
            case "firstordefault":
            {
                var context = Visit(node.Context);

                var genericArguments = context.Type.GetTypeGenericArguments();

                var arguments = node.Arguments.Select(argument => argument.NodeType == ExpressionType.Lambda ? VisitLambda((ODataLambdaExpression)argument, genericArguments[0]) : Visit(argument)).ToList();

                arguments.Insert(0, context);

                var type = context.Type.IsIQueryable() ? typeof(Queryable) : typeof(Enumerable);

                return(Expression.Call(type, functionName, genericArguments, arguments.ToArray()));
            }

            case "isof":
            {
                //ParameterExpression parameter = null;
                Expression genericParameter = null;
                object     typeName         = null;
                if (node.Arguments.Length == 1)
                {
                    genericParameter = _parameters.Select(x => x.Value).FirstOrDefault();
                    typeName         = ((ConstantExpression)Visit(node.Arguments[0])).Value;
                }

                if (node.Arguments.Length == 2)
                {
                    var arguments = node.Arguments.Select(Visit).ToArray();
                    genericParameter = arguments[0];
                    typeName         = ((ConstantExpression)arguments[1]).Value;
                }

                if (genericParameter != null)
                {
                    var type = typeName is Type ? (Type)typeName : null;
                    if (type == null && KnownAssembly != null)
                    {
                        type = KnownAssembly.GetType((string)typeName);
                    }
                    return(Expression.TypeIs(genericParameter, type));
                }


                throw new NotSupportedException(node.DebugView());
            }

            case "cast":
            {
                if (node.Arguments.Length == 1)
                {
                    var parameter = _parameters.Select(x => x.Value).FirstOrDefault();
                    if (parameter != null)
                    {
                        var type = (Type)((ConstantExpression)Visit(node.Arguments[0])).Value;

                        return(Expression.TypeAs(parameter, type));
                    }
                }

                if (node.Arguments.Length == 2)
                {
                    var arguments = node.Arguments.Select(Visit).ToArray();

                    return(Expression.TypeAs(arguments[0], (Type)((ConstantExpression)arguments[1]).Value));
                }

                throw new NotSupportedException(node.DebugView());
            }

            case "substringof":
            {
                var right = Visit(node.Arguments[1]);
                var left  = Visit(node.Arguments[0]);

                return(Expression.Call(right, MethodProvider.ContainsMethod, new[] { left }));
            }

            default:
            {
                if (node.Context != null)
                {
                    var context = Visit(node.Context);

                    var arguments = node.Arguments.Select(Visit).ToList();

                    var expr = ExpressionMethodBinder.Bind(context.Type, functionName, context, arguments.ToArray());

                    if (expr == null)
                    {
                        throw new Exception(String.Format("Can't find a method: {0}", node.DebugView()));
                    }

                    return(expr);
                }

                if (MethodProvider.UserFunctions.ContainsKey(node.MethodName))
                {
                    var arguments = node.Arguments.Select(Visit).ToArray();

                    var methodExpression = ExpressionMethodBinder.Bind(
                        MethodProvider.UserFunctions[node.MethodName], node.MethodName, null, arguments);

                    if (methodExpression == null)
                    {
                        throw new Exception(String.Format("Can't find a method: {0}", node.DebugView()));
                    }

                    return(methodExpression);
                }

                throw new NotSupportedException(String.Format("Can't find a method: {0}", node.DebugView()));
            }
            }
        }