/// <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); }