예제 #1
0
        private ODataExpression TranslateInternal(Expression linq)
        {
            if (linq == null)
            {
                return(null);
            }

            // captured parameters
            object value;

            if (TryGetValueFast(linq, out value))
            {
                // special case the handling of queryable constants, since these can be the "root"
                // of the query expression tree. We check for isInsideQuery since we don't allow multiple
                // roots. An example of a multiply-rooted tree would be: q.Where(x => q.Any(xx => xx.A < x.A))
                if (!this._isInsideQuery && typeof(IQueryable).GetTypeInfo().IsAssignableFrom(linq.Type.GetTypeInfo()))
                {
                    this._rootQuery = (IQueryable)value;
                    return(ODataExpression.Query());
                }
                return(ODataExpression.Constant(value, linq.Type));
            }

            switch (linq.NodeType)
            {
            case ExpressionType.OrElse:
                return(this.TranslateBinary(linq, ODataBinaryOp.Or));

            case ExpressionType.AndAlso:
                return(this.TranslateBinary(linq, ODataBinaryOp.And));

            case ExpressionType.Add:
                if (linq.Type == typeof(string))
                {
                    // special case adding strings, since that's the same as Concat
                    var binary = (BinaryExpression)linq;
                    return(this.TranslateInternal(Expression.Call(Helpers.GetMethod(() => string.Concat(default(string), default(string))), binary.Left, binary.Right)));
                }
                return(this.TranslateBinary(linq, ODataBinaryOp.Add));

            case ExpressionType.Subtract:
                return(this.TranslateBinary(linq, ODataBinaryOp.Subtract));

            case ExpressionType.Multiply:
                return(this.TranslateBinary(linq, ODataBinaryOp.Multiply));

            case ExpressionType.Divide:
                return(this.TranslateBinary(linq, ODataBinaryOp.Divide));

            case ExpressionType.Modulo:
                return(this.TranslateBinary(linq, ODataBinaryOp.Modulo));

            case ExpressionType.Equal:
                return(this.TranslateBinary(linq, ODataBinaryOp.Equal));

            case ExpressionType.NotEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.NotEqual));

            case ExpressionType.LessThan:
                return(this.TranslateBinary(linq, ODataBinaryOp.LessThan));

            case ExpressionType.LessThanOrEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.LessThanOrEqual));

            case ExpressionType.GreaterThan:
                return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThan));

            case ExpressionType.GreaterThanOrEqual:
                return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThanOrEqual));

            case ExpressionType.Not:
                return(ODataExpression.UnaryOp(this.TranslateInternal(((UnaryExpression)linq).Operand), ODataUnaryOp.Not));

            case ExpressionType.Negate:
                // we translate -a.B as (-1 * a.B) for numeric types
                var negated = ((UnaryExpression)linq).Operand;
                var underlyingNegatedType = Nullable.GetUnderlyingType(negated.Type) ?? negated.Type;
                if (!underlyingNegatedType.IsNumeric())
                {
                    throw new ODataCompileException("Expression '" + linq + "' could not be translated to OData: only negation of numeric types is supported");
                }
                var multiplyNegate = Expression.Multiply(
                    Expression.Constant(Convert.ChangeType(-1, underlyingNegatedType), negated.Type),
                    negated
                    );
                return(this.TranslateInternal(multiplyNegate));

            case ExpressionType.MemberAccess:
                return(this._memberAndParameterTranslator.TranslateMemberAccess((MemberExpression)linq));

            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
            case ExpressionType.TypeAs:
                return(ODataExpression.Convert(this.TranslateInternal(((UnaryExpression)linq).Operand), linq.Type));

            case ExpressionType.Parameter:
                return(this._memberAndParameterTranslator.TranslateParameter((ParameterExpression)linq));

            case ExpressionType.TypeIs:
                var typeBinary     = (TypeBinaryExpression)linq;
                var isArg          = this.TranslateInternal(typeBinary.Expression);
                var isTypeConstant = ODataExpression.Constant(typeBinary.TypeOperand);
                return(isArg != null
                                                ? ODataExpression.Call(ODataFunction.IsOf, new[] { isArg, isTypeConstant })
                                                : ODataExpression.Call(ODataFunction.IsOf, new[] { isTypeConstant }));

            case ExpressionType.Call:
                return(this.TranslateCall((MethodCallExpression)linq));

            case ExpressionType.Quote:
                var quoted = (LambdaExpression)((UnaryExpression)linq).Operand;
                if (!this._isInsideQuery)
                {
                    throw new ODataCompileException("Unexpected placement for lambda expression " + linq);
                }
                return(this.TranslateInternal(quoted.Body));

            default:
                throw new ODataCompileException("Expression '" + linq + "' of type " + linq.NodeType + " could not be translated to OData");
            }
        }