Home > other >  lambda expression function body to sql query
lambda expression function body to sql query

Time:04-04

I would like to know if there is any way to do the next. I would like to extract compiled body of a function.

class Program
{
    public static void Main(string[] args)
    {
        int i = 5;
        Where2(p => new { V = p.Length == i });
        
    }

    public static void Where2<TResult>(Expression<Func<string, TResult>> func)
    {
        try
        {

            var body = (System.Linq.Expressions.NewExpression)func.Body;
            
            Console.WriteLine("Output:");
            Console.WriteLine(body);
            Console.ReadLine();

        }
        catch (Exception e)
        {

            throw e;
        }
        //dynamic operation = func.Body;   //

    }
}

Output: new <>f__AnonymousType0`1(V = (p.Length == value(TestDelegateFun.Program <>c__DisplayClass0_0).i))

So I would like to have the compiled body like :

Output: new <>f__AnonymousType0`1(V = (p.Length == 5))

CodePudding user response:

public class FilterConstructor : ExpressionVisitor
    {
        private static readonly Dictionary<ExpressionType, string> _logicalOperators;
        private static readonly Dictionary<Type, Func<object, string>> _typeConverters;
        static FilterConstructor()
        {
            //mappings for table, shown above
            _logicalOperators = new Dictionary<ExpressionType, string>
            {
                [ExpressionType.Not] = "not",
                [ExpressionType.GreaterThan] = "gt",
                [ExpressionType.GreaterThanOrEqual] = "ge",
                [ExpressionType.LessThan] = "lt",
                [ExpressionType.LessThanOrEqual] = "le",
                [ExpressionType.Equal] = "==",
                [ExpressionType.Not] = "not",
                [ExpressionType.AndAlso] = "and",
                [ExpressionType.OrElse] = "or"
            };

            //if type is string we will wrap it into single quotes
            //if it is a DateTime we will format it like datetime'2008-07-10T00:00:00Z'
            //bool.ToString() returns "True" or "False" with first capital letter, so .ToLower() is applied
            //if it is one of the rest "simple" types we will just call .ToString() method on it
            _typeConverters = new Dictionary<Type, Func<object, string>>
            {
                [typeof(string)] = x => $"'{x}'",
                [typeof(DateTime)] =
                  x => $"'{((DateTime)x).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")}'",
                [typeof(bool)] = x => x.ToString().ToLower()
            };
        }

        private StringBuilder _queryStringBuilder;
        private Stack<string> _fieldNames;
        public FilterConstructor()
        {
            //here we will collect our query
            _queryStringBuilder = new StringBuilder();
            //will be discussed below
            _fieldNames = new Stack<string>();
        }

        //entry point
        public string GetQuery(LambdaExpression predicate)
        {
            
            Visit(predicate.Body);
            var query = _queryStringBuilder.ToString();
            _queryStringBuilder.Clear();

            return query;
        }

        //protected override Expression VisitUnary(UnaryExpression node)
        //{
        //    //assume we only allow not (!) unary operator:
        //    if (node.NodeType != ExpressionType.Not)
        //        throw new NotSupportedException("Only not(\"!\") unary operator is supported!");

        //    _queryStringBuilder.Append($"{_logicalOperators[node.NodeType]} ");//!

        //    _queryStringBuilder.Append("(");                                   //(!
        //                                                                       //go down from a tree
        //    Visit(node.Operand);                                               //(!expression
        //    _queryStringBuilder.Append(")");                                   //(!expression)

        //    //we should return expression, it will allow to create new expression based on existing one,
        //    //but, at our case, it is not needed, so just return initial node argument
        //    return node;
        //}

        //corresponds to: and, or, greater than, less than, etc.
        protected override Expression VisitBinary(BinaryExpression node)
        {
            _queryStringBuilder.Append("(");                                    //(
                                                                                //left side of binary operator
            Visit(node.Left);                                                   //(leftExpr

            _queryStringBuilder.Append($" {_logicalOperators[node.NodeType]} ");//(leftExpr and

            //right side of binary operator
            Visit(node.Right);                                                  //(leftExpr and RighExpr
            _queryStringBuilder.Append(")");                                    //(leftExpr and RighExpr)

            return node;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            

            if (node.Expression.NodeType == ExpressionType.Constant
               ||
               node.Expression.NodeType == ExpressionType.MemberAccess)
            {
                _fieldNames.Push(node.Member.Name);
                Visit(node.Expression);
            }
            else
                //corresponds to: x.Customer - just write "Customer"
                _queryStringBuilder.Append(node.Member.Name);
            return node;
        }

        //corresponds to: 1, "Tom", instance of NameSpace <>c__DisplayClass12_0, instance of Order, i.e.
        //any expression with value
        protected override Expression VisitConstant(ConstantExpression node)
        {
            //just write value
            _queryStringBuilder.Append(GetValue(node.Value));
            return node;
        }

        private string GetValue(object input)
        {
            var type = input.GetType();
            //if it is not simple value
            if (type.IsClass && type != typeof(string))
            {
                //proper order of selected names provided by means of Stack structure
                var fieldName = _fieldNames.Pop();
                var fieldInfo = type.GetField(fieldName);
                object value;
                if (fieldInfo != null)
                    //get instance of order    
                    value = fieldInfo.GetValue(input);
                else
                    //get value of "Customer" property on order
                    value = type.GetProperty(fieldName).GetValue(input);
                return GetValue(value);
            }
            else
            {
                //our predefined _typeConverters
                if (_typeConverters.ContainsKey(type))
                    return _typeConverters[type](input);
                else
                    //rest types
                    return input.ToString();
            }
        }
    }
  • Related