【问题标题】:Creating dynamic lambda using helper method使用辅助方法创建动态 lambda
【发布时间】:2016-09-19 08:57:43
【问题描述】:

我有一个为给定实体创建基础搜索条件的主要方法。因此,在这种方法中,我会在将其应用于查询之前检查默认值。

例如

        if (!string.IsNullOrEmpty(value))
            qry = qry.Where(x => x.PropA.Contains(value));

        if (!string.IsNullOrEmpty(anotherValue))
            qry = qry.Where(x => x.PropB.Contains(anotherValue));

但是,我想重构它并改用辅助方法,但由于我对表达式的知识和经验有限,我很难完成任务。

我有这个锅炉代码,我相信它说明了我想要完成的事情:

    IQueryable<T> Test<T, TV>(IQueryable<T> qry, Expression<Func<T, TV>> prop, TV value)
    {
        if (EqualityComparer<TV>.Default.Equals(value, default(TV)))
            return qry;

        var right = Expression.Constant(value);

        var body = Expression.Equal(prop, right);
        var lambda = Expression.Lambda<Func<T, bool>>(body);

        return qry.Where(lambda);
    }

这应该使我能够拨打这样的电话:

qry = Test(qry, x=>PropA, value);
qry = Test(qry, x=>PropB, anotherValue);

但问题是 body 变量会导致 BinaryExpression,我完全不知道如何从这里开始。

【问题讨论】:

  • 如果默认值是-1 而不是0null 怎么办?这种方法真的提高了可读性吗?我更喜欢你的简单代码,我看到if而不是隐藏的if“单线”。只是我的意见。

标签: c# lambda expression


【解决方案1】:

您必须将方法转换为表达式,然后将其作为 lambda 的主体。

因此,从您的锅炉代码开始,经过上述更改后,它应该看起来像

    IQueryable<T> Test<T, TV>(IQueryable<T> qry, Expression<Func<T, TV>> prop, string propertyValue)
    {

        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        var someValue = Expression.Constant(propertyValue, typeof(string));
        var body = Expression.Call(prop, method, someValue); // pseudocode, to be refined below

        var lambda = Expression.Lambda<Func<T, bool>>(body);

        return qry.Where(lambda);
    }

现在让我用字符串访问器重新表述它

    static IQueryable<T>  Test<T>(IQueryable<T> qry, string propertyName, string propertyValue)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");
        var propertyExp = Expression.Property(parameterExp, propertyName);
        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        var someValue = Expression.Constant(propertyValue, typeof(string));
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);
        var lambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
        return qry.Where(lambda);
    }

最后是一个简单的用法示例

    class MyClass
    {
        public string Myname { get; set; }
    }

    static void Main(string[] args)
    {
        var check = new MyClass() { Myname = "11 aa 22" };
        var check2 = new MyClass() { Myname = "11 bb 22" };
        var x = new List<MyClass>();
        x.Add(check);
        x.Add(check2);
        var q = x.AsQueryable();
        var qry = Test(q, "Myname", "bb");
    }

好吧,如果你更喜欢一个属性选择器,那么助手就会变成

    static IQueryable<T>  Test<T>(IQueryable<T> qry, Expression<Func<T, string>> selector, string propertyValue)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");

        var memberExpression = (MemberExpression)selector.Body;
        var parameterTProperty = (PropertyInfo)memberExpression.Member;
        var propertyExp = Expression.Property(parameterExp, parameterTProperty);

        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        var someValue = Expression.Constant(propertyValue, typeof(string));
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);
        var lambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
        return qry.Where(lambda);
    }

用作

        var qry = Test(q, z => z.Myname , "bb");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多