使用lambda表达式动态设置比较运算符

时间:2017-06-24 17:08:57

标签: c# linq lambda expression

要动态设置linq查询中的比较运算符,请执行以下操作:

parameter = Expression.Parameter(typeof(SomeType));
var predicate = Expression.Lambda<Func<SomeType, bool>>(
    Combine(
        "=",
        Expression.Property(parameter, "ID"),
        Expression.Constant(150497)
    ), parameter);

BinaryExpression Combine(string op, Expression left, Expression right)
{
    switch (op)
    {
        case "=":
            return Expression.Equal(left, right);
        case "<":
            return Expression.LessThan(left, right);
        case ">":
            return Expression.GreaterThan(left, right);
    }
    return null;
}

有效。但我宁愿将lambda表达式作为参数“left”传递。那可能吗?类似的东西:

var predicate = Expression.Lambda<Func<SomeType, bool>>(Combine(
    "=",
    c => c.ID,
    Expression.Constant(150497)
), parameter);

2 个答案:

答案 0 :(得分:1)

这个怎么样?不幸的是,我现在无法测试它,所以请告诉我它是否不起作用

private class TestCalss
{
    public int Id { get; set; }
}

private class SwapVisitor : ExpressionVisitor
{
    public readonly Expression _from;
    public readonly Expression _to;

    public SwapVisitor(Expression from, Expression to)
    {
        _from = from;
        _to = to;
    }

    public override Expression Visit(Expression node) => node == _from ? _to : base.Visit(node);
}

private BinaryExpression Combine<T, TResult>(string op, Expression<Func<T, TResult>> left, Expression right, ParameterExpression parameter)
{
    // Need to use parameter from outer lambda expression for equality two expressions 
    var swap = new SwapVisitor(left.Parameters[0], parameter);
    var newLeft = swap.Visit(left) as Expression<Func<T, TResult>>;
    switch (op)
    {
        case "=":
            return Expression.Equal(newLeft.Body, right);
        case "<":
            return Expression.LessThan(newLeft.Body, right);
        case ">":
            return Expression.GreaterThan(newLeft.Body, right);
    }
    return null;
}

...
var parameter = Expression.Parameter(typeof(TestCalss));
var predicate = Expression.Lambda<Func<TestCalss, bool>>(
    Combine<TestCalss, int>("=", c => c.Id, Expression.Constant(156), parameter),
    parameter);
var test = new TestCalss { Id = 156 };
var result = predicate.Compile()(test); // <- true

答案 1 :(得分:0)

基本上,您希望在不使用字符串的情况下访问类的字段,如果您的字段是公共的,那么这是可行的。

Here你可以看到一个很好的例子。

至于你对它的具体用法,它有点像:

public class Test
{

    public static void someMethod()
    {
        var parameter = Expression.Parameter(typeof(SomeType));
        var predicate = Expression.Lambda<Func<SomeType, bool>>(Combine(
                            "=",
                            Expression.Parameter(typeof(int), GetMemberName((SomeType c) => c.ID)),
                            Expression.Constant(150497)
                        ), parameter);
    }

    public static BinaryExpression Combine(string op, Expression left, Expression right)
    {
        switch (op)
        {
            case "=":
                return Expression.Equal(left, right);
            case "<":
                return Expression.LessThan(left, right);
            case ">":
                return Expression.GreaterThan(left, right);
        }
        return null;
    }

    public static string GetMemberName<T, TValue>(Expression<Func<T, TValue>> memberAccess)
    {
        return ((MemberExpression)memberAccess.Body).Member.Name;
    }

}

public class SomeType
{
    public int ID { get; set; }
    private string aString;
}

免责声明:没有测试,但逻辑就在那里。

如上所述,您将无法访问SomeType.aString,因为它是private。我也放typeof(int),但如果你想要它是动态的,你可以使用另一种方法(即GetMemberType)来获得字段的类型。