如何将谓词表达式分解为查询

本文关键字:分解 查询 表达式 谓词 | 更新日期: 2023-09-27 18:09:18

我有以下类Person,带有自定义的Where方法:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Where(Expression<Func<Person, bool>> predicate)
    {
        return String.Empty;
    }
}

如何以可枚举的方式检索Expression参数名称和值?

Person p = new Person();
p.Where(i => i.Name == "Shlomi" && i.Age == 26);

用于构建字符串查询,其中根据表达式的名称和值附加参数。

// Eventually I will convert the above into the following:
string Query = "select * from person where name = @Name AND Age = @Age";
SqlParameter[] param = new SqlParameter[] { 
    new SqlParameter("@Name","Shlomi"),
    new SqlParameter("@Age","26")
};

如何将谓词表达式分解为查询

我当然认为你应该遵循StriplingWarrior的建议,使用LINQ to Entities或LINQ to SQL,但为了重新发明轮子(很糟糕(,我将以我之前的答案为基础。

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

现在,您需要遍历表达式树,访问每个候选表达式,并确定它是否包括MemberExpression。下面的GetMemberExpressions方法将遍历表达式树,并检索在中找到的每个MemberExpression

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

问题在于表达式实际上是一棵树。

例如,您有以下谓词:

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

如果您检查expr,您会看到它的主体具有"AndAlso"NodeType,并且它还具有操作数的两个属性——LeftRight,它们分别指向另一个具有"Equal"NodeTypeExpression对象,而这两个对象又具有两个属性LeftRight,分别指向类型为MemberAccessConstantExpression

虽然您可以处理该树并提取所需的所有信息,但最终您将实现自己的LINQ2SQL提供程序,例如重新设计轮子。如果你有这种感觉,我希望我提供了足够的信息来开始挖掘。。。

您想要做的事情非常复杂,而且有整个框架都是为实现这一点而构建的,因此您不必自己编写逻辑。看看LINQ to Entities和LINQ to SQL,例如