如何创建表达式树来表示'p => p.a.tostring()。在C#中包含(“s”)'?

时间:2017-05-20 07:43:30

标签: c# expression-trees

我有IQueryable类型的数据集合,我抽象了一个方法,用作全场模糊查询,在开始时,我是逐个属性名称来编写的。如下代码:

private IQueryable<Tables1> FilterResult(string search, List<Tables1> dtResult)
    {
        IQueryable<Tables1> results = dtResult.AsQueryable();
        results = results.Where(p => (
            search == null || (
                p.Name != null && p.Name.Contains(search) ||
                p.age != null && p.age.ToString().Contains(search) ||
                p.sex != null && p.sex.Contains(search) ||
                p.content1 != null && p.content1.Contains(search) ||
                p.content2 != null && p.content2.Contains(search) ||
                p.content3 != null && p.content3.Contains(search)
                )
            ));
        return results;
    }

但是,如果传入的List集合的类型发生变化,则写入,则所有物理属性都必须重新写入。所以我改变了T的类型:

private IQueryable<T> FilterResult(string search, List<T> dtResult,T t)
    {
        IQueryable<T> results = dtResult.AsQueryable();
        //do something
        return results;
    }

这背后的想法是通过反射获得传入T类型的所有属性。然后通过表达式树构建Lambda表达式。

问题是如何构建'p =&gt; p.age.ToString()。通过表达式树包含(搜索)?

以下是完整的代码:

    private IQueryable<T> FilterResult(string search, List<T> dtResult, T t) 
    {
        List<Expression> tempExp = new List<Expression>();
        var parameter = Expression.Parameter(typeof(T), "p");
        foreach (var mi in t.GetType().GetProperties())
        {
            Expression left = Expression.Property(parameter, t.GetType().GetProperty(mi.Name));
            Expression right = Expression.Constant(search, typeof(string));
            MethodInfo method;
            MethodCallExpression exp;
            if (mi.PropertyType == typeof(Int32) || mi.PropertyType == typeof(Int64))
            {
                //this code is wrong
                var exp1 = Expression.Call(Expression.Convert(left, typeof(string)), typeof(object).GetMethod("ToString"));
                method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                exp = Expression.Call(exp1, method, right);

            }
            else
            {
                method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                exp = Expression.Call(left, method, right);
            }
            tempExp.Add(exp);
        }
        Expression all = Expression.Or(Expression.Equal(Expression.Constant(search), null), tempExp[0]);
        for (int i = 1; i < tempExp.Count; i++)
        {
            all = Expression.Or(all, tempExp[i]);
        }
        var lambda = Expression.Lambda<Func<T, bool>>(all, parameter);
        var results = dtResult.Where(lambda.Compile()).AsQueryable(); ;
        return results;
    }

1 个答案:

答案 0 :(得分:0)

我会说它可以更容易。我已将您的public class Tables { public int Age { get; set; } public string Name { get; set; } public string Content { get; set; } } public class Matcher { private static readonly PropertyInfo[] Properties = typeof(Tables).GetRuntimeProperties().ToArray(); public IQueryable<Tables> FilterResult(string search, List<Tables> dtResult) { if(search == null) //Consider using string.IsNullOrWhiteSpace(search) but I wasn't sure if you want to avoid searching for spaces { return dtResult.AsQueryable(); } return dtResult.Where(p => IsMatch(p, search)).AsQueryable(); } private static bool IsMatch(Tables tables, string search) { foreach (var propertyInfo in Properties) { var value = propertyInfo.GetValue(tables); if (value != null && value.ToString().Contains(search)) { return true; } } return false; } } 模型简化为演示文稿。

class Program
{
    public static void Main()
    {
        const string search = "Bob";
        var matcher = new Matcher();
        var items = new List<Tables>
        {
            new Tables {Content = string.Empty, Name = "Bob"}, //This will match
            new Tables {Content = "Bob is the best guy.", Name = "Joe"}, //This will also match
            new Tables {Content = "Something", Name = null} // This won't null name to verify that nothing unexpected will happen
        };

        var results = matcher.FilterResult( search, items );
        foreach ( var result in results )
        {
            Console.WriteLine($"Matched the guy named {result.Name}");
        }
        Console.ReadKey();
    }
}

我们可以在这里投入使用:

public class Matcher<T>
{
    private static readonly PropertyInfo[] Properties = typeof(T).GetRuntimeProperties().ToArray();

    public IQueryable<T> FilterResult(string search, List<T> items)
    {
        if ( search == null) //Consider using string.IsNullOrWhiteSpace(search) but I wasn't sure if you want to avoid searching for spaces
        {
            return items.AsQueryable();
        }

        return items.Where(p => IsMatch(p, search)).AsQueryable();
    }

    private static bool IsMatch(T item, string search)
    {
        foreach (var propertyInfo in Properties)
        {
            var value = propertyInfo.GetValue(item);
            if (value != null && value.ToString().Contains(search))
            {
                return true;
            }
        }

        return false;
    }
}

编辑: 这是通用版本

config.vm.box = "ubuntu/precise64"