动态构建Linq以使用连锁属性实现Lambda表达式

时间:2013-11-30 05:44:55

标签: c# lambda linq-to-entities

我最近遇到了为Kendo UI网格控件实现服务器端过滤的要求。由于linq to entities lambda表达式中的比较运算符在编译时不可知,因此有必要动态构建表达式。我以前从未有过这样的需要,但是我做了足够的挖掘来弄清楚如何从表达式树构建简单的lambda表达式。例如,我为people.Where(person => person.LastName == "Doe")之类的简单过滤器构建表达式,如下所示:

ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression left = Expression.Property(filterParam, "LastName");
ConstantExpression right = Expression.Constant("Doe", typeof(string));
BinaryExpression predicate = Expression.Equal(left, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>(predicate, filterParam);

IQueryable<Person> filtered = people.Where(lambda);

好的,等等问题。我遇到了需要做类似上面的事情,除了用全名而不是姓氏过滤。我的数据库中没有全名的字段 - 只是单独的名字和姓氏字段,所以我不能为此引用单个属性。我需要连接表达式中的两个属性,以便生成的lambda看起来像:

people.Where(person => (person.firstName + person.LastName) == "JohnDoe")

我无法找到一个示例,展示如何从表达式树动态构建lambda。我认为它必须是可能的 - 它似乎并不像一个场景那么疯狂,是吗?

编辑:我也有兴趣知道该怎么做:

people.Where(person => (person.FirstName + " " + person.LastName) == "John Doe")

Edit2:这个问题的解决方案是国王回答的一个非常简单的改变:

ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression first = Expression.Property(filterParam, "FirstName");
MemberExpression last = Expression.Property(filterParam, "LastName");
Type strType = typeof(string);

MethodCallExpression concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { strType, strType, strType }),
    first, Expression.Constant(" ", strType), last);

ConstantExpression right = Expression.Constant("John Doe", strType);
BinaryExpression predicate = Expression.Equals(concat, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>>(predicate, filterParam);

IQueryable<Person> filtered = people.Where(lambda);

2 个答案:

答案 0 :(得分:2)

您可以尝试添加MethodCallExpression以使用string.Concat方法:

var firstName = Expression.Property(filterParam, "FirstName");
var lastName = Expression.Property(filterParam, "LastName");
var concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { typeof(string), typeof(string) }),
                             firstName, lastName);
BinaryExpression predicate = Expression.Equal(concat, right);
//....

答案 1 :(得分:1)

为了使这更容易和可重用,我使用PropertyTranslator结合QueryInterceptor在实体模型上定义了额外的属性。

使用此代码,您可以在Entity模型上定义FullName属性,如下所示:

public class Person
{
    private static readonly CompiledExpressionMap<Employee, string> FullNameExpr =
        DefaultTranslationOf<Employee>.Property(e => e.FullName).Is(e => e.FirstName + " " + e.LastName);

    public string FirstName { get; set; }

    public string LastName { get; set; }

    [NotMapped]
    public string FullName
    {
        get
        {
            return FullNameExpr.Evaluate(this);
        }
    }
}

只需在查询中使用此Fullname属性,如:

IQueryable<Person> filtered = people
    .InterceptWith(new PropertyVisitor()).AsQueryable()
    .Where(p => p.FullName == "John Doe");

我不确定您是否可以在设计中实现此功能,但如果可能,请调查此选项。

您可以查看示例项目here