Exemple #1
0
        /// <summary>
        /// Visit :: x => x.Customer.Name.`ToUpper()`
        /// </summary>
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            // get method declaring type - if is from any kind of list, read as Enumerable
            var isList = Reflection.IsList(node.Method.DeclaringType);

            var declaringType = isList ? typeof(Enumerable) : node.Method.DeclaringType;

            // if special method for index access, eval index value (do not use parameters)
            if (this.IsMethodIndexEval(node, out var obj, out var idx))
            {
                this.Visit(obj);

                var index = this.Evaluate(idx, typeof(string), typeof(int));

                if (index is string)
                {
                    _builder.Append(".");
                    _builder.Append($"['{index}']");
                }
                else
                {
                    _builder.Append($"[{index}]");
                }

                return(node);
            }

            // if not found in resolver, try run method
            if (!_resolver.TryGetValue(declaringType, out var type))
            {
                // if method are called by parameter expression and it's not exists, throw error
                var isParam = ParameterExpressionVisitor.Test(node);

                if (isParam)
                {
                    throw new NotSupportedException($"Method {node.Method.Name} not available to convert to BsonExpression ({node.ToString()}).");
                }

                // otherwise, try compile and execute
                var value = this.Evaluate(node);

                base.Visit(Expression.Constant(value));

                return(node);
            }

            // otherwise I have resolver for this method
            var pattern = type.ResolveMethod(node.Method);

            if (pattern == null)
            {
                throw new NotSupportedException($"Method {Reflection.MethodName(node.Method)} in {node.Method.DeclaringType.Name} are not supported when convert to BsonExpression ({node.ToString()}).");
            }

            // run pattern using object as # and args as @n
            this.ResolvePattern(pattern, node.Object, node.Arguments);

            return(node);
        }
        public string ResolveMethod(MethodInfo method)
        {
            // all methods in Enumerable are Extensions (static methods), so first parameter is IEnumerable
            var name = Reflection.MethodName(method, 1);

            switch (name)
            {
            // get all items
            case "AsEnumerable()": return("@0[*]");

            // get fixed index item
            case "get_Item(int)": return("#[@0]");

            case "ElementAt(int)": return("@0[@1]");

            case "Single()":
            case "First()":
            case "SingleOrDefault()":
            case "FirstOrDefault()": return("@0[0]");

            case "Last()":
            case "LastOrDefault()": return("@0[-1]");

            // get single item but with predicate function
            case "Single(Func<T,TResult>)":
            case "First(Func<T,TResult>)":
            case "SingleOrDefault(Func<T,TResult>)":
            case "FirstOrDefault(Func<T,TResult>)": return("FIRST(@0[@1])");

            case "Last(Func<T,TResult>)":
            case "LastOrDefault(Func<T,TResult>)": return("LAST(@0[@1])");

            // filter
            case "Where(Func<T,TResult>)": return("@0[@1]");

            // map
            case "Select(Func<T,TResult>)": return("(@0 => @1)");

            // aggregate
            case "Count()": return("COUNT(@0)");

            case "Sum()": return("SUM(@0)");

            case "Average()": return("AVG(@0)");

            case "Max()": return("MAX(@0)");

            case "Min()": return("MIN(@0)");

            // aggregate with map function
            case "Count(Func<T,TResult>)": return("COUNT(@0 => @1)");

            case "Sum(Func<T,TResult>)": return("SUM(@0 => @1)");

            case "Average(Func<T,TResult>)": return("AVG(@0 => @1)");

            case "Max(Func<T,TResult>)": return("MAX(@0 => @1)");

            case "Min(Func<T,TResult>)": return("MIN(@0 => @1)");

            // convert to array
            case "ToList()":
            case "ToArray()": return("ARRAY(@0)");

            // any/all special cases
            case "Any(Func<T,TResult>)": return("@0 ANY %");

            case "ALL(Func<T,TResult>)": return("@0 ANY %");
            }
            ;

            return(null);
        }