我最近遇到了为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);
答案 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。