How do I decompose a Predicate Expression into a query?

后端 未结 3 1826
一个人的身影
一个人的身影 2020-12-30 16:04

I have the following class Person, with a custom Where method:

public class Person
{
    public string Name { get; set; }
    publi         


        
相关标签:
3条回答
  • 2020-12-30 16:14

    The problem is that the Expression is actually a tree.

    For example, you have the following predicate:

    Expression<Func<Person, bool>> expr = x => x.Name == "Shlomi" && x.Age == 26;
    

    If you inspect expr, you'll see that it's body has "AndAlso" NodeType and it also has two properties for operands -- Left and Right each pointing to another Expression object with "Equal" NodeType which in turn again have two properties Left and Right which point to Expression of type MemberAccess and Constant respectively.

    While you can process that tree and extract all information you need, you'll end up implementing your own LINQ2SQL provider, e.g. reinventing a wheel. If you feel so, I hope I provided enough information to start digging...

    0 讨论(0)
  • 2020-12-30 16:15

    What you're wanting to do is very complicated, and there are entire frameworks that are built to do this, so you don't have to write the logic yourself. Take a look at LINQ to Entities and LINQ to SQL, e.g.

    0 讨论(0)
  • 2020-12-30 16:18

    I certainly think you should follow StriplingWarrior's advice and use LINQ to Entities or LINQ to SQL, but for the sake of reinventing the wheel (poorly) I'll build off of a prior answer of mine.

    // Start with a method that takes a predicate and retrieves the property names
    static IEnumerable<string> GetColumnNames<T>(Expression<Func<T,bool>> predicate)
    {
        // Use Expression.Body to gather the necessary details
        var members = GetMemberExpressions(predicate.Body);
        if (!members.Any())
        {
            throw new ArgumentException(
                "Not reducible to a Member Access", 
                "predicate");
        }
    
        return members.Select(m => m.Member.Name);
    }
    

    Now you need to walk the expression tree, visiting each candidate expression, and determine if it includes a MemberExpression. The GetMemberExpressions method below will walk an expression tree and retrieve each of the MemberExpressions found within:

    static IEnumerable<MemberExpression> GetMemberExpressions(Expression body)
    {
        // A Queue preserves left to right reading order of expressions in the tree
        var candidates = new Queue<Expression>(new[] { body });
        while (candidates.Count > 0)
        {
            var expr = candidates.Dequeue();
            if (expr is MemberExpression)
            {
                yield return ((MemberExpression)expr);
            }
            else if (expr is UnaryExpression)
            {
                candidates.Enqueue(((UnaryExpression)expr).Operand);
            }
            else if (expr is BinaryExpression)
            {
                var binary = expr as BinaryExpression;
                candidates.Enqueue(binary.Left);
                candidates.Enqueue(binary.Right);
            }
            else if (expr is MethodCallExpression)
            {
                var method = expr as MethodCallExpression;
                foreach (var argument in method.Arguments)
                {
                    candidates.Enqueue(argument);
                }
            }
            else if (expr is LambdaExpression)
            {
                candidates.Enqueue(((LambdaExpression)expr).Body);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题