我们说我通过LINQ查询创建了一些匿名类型:
var query = from Person in db.People
join Pet in Pets on Person.ID equals Pet.PersonID
join Thingy in Thingies on Person.ID equals Thingy.PersonID
select new {
Person.ID,
PetName = Pet.Name,
Thing = Thingy.Thing,
OtherThing = Thingy.OtherThing
};
现在我想通过动态构建谓词来将一些复杂的表达式应用于查询。我一直在为已知类型做的事情是这样的:
var predicate = PredicateBuilder.False<MyType>();
predicate = predicate.Or(myType => myType.Flag);
if (someCondition) {
var subPredicate = PredicateBuilder.True<MyType>();
subPredicate = subPredicate.And(myType => myType.Thing == someThing);
subPredicate = subPredicate.And(myType => myType.OtherThing == someOtherThing);
predicate = predicate.Or(subPredicate);
}
query = query.Where(predicate);
是否可以使用匿名类型执行类似的操作?我还没有找到一种使用PredicateBuilder
匿名类型的方法,而且我不熟悉任何其他可以动态构建这样的表达式的构造。如果没有,在为查询动态生成嵌套表达式时,我应该采取另一种方法吗?
答案 0 :(得分:3)
对于匿名类型,假设您只需要PredicateBuilder.False<T>
和PredicateBuilder.True<T>
,就可以这样做:
private static Expression<Func<T,bool>> MakeTrue(IQueryable<T> ignored) {
return PredicateBuilder.True<T>();
}
private static Expression<Func<T,bool>> MakeFalse(IQueryable<T> ignored) {
return PredicateBuilder.False<T>();
}
现在您可以重写代码而无需明确使用匿名类型:
var predicate = MakeFalse(query);
predicate = predicate.Or(v => v.ID > 10000);
query = query.Where(predicate);
我们的想法是让编译器为您做类型推断。不幸的是,你最终得到了一个未使用的方法参数。
答案 1 :(得分:3)
在一般情况下,在这里使用PredicateBuilder
可能会很尴尬;直接使用Expression
API可能更容易。你需要得到代表匿名类型的Type
,构建表达式而不用一个T
,然后翻转成通用代码来构建最终的lambda。幸运的是,不是太棘手:
var p = Expression.Parameter(GetQueryType(query));
var body = Expression.And(
Expression.Equal(Expression.PropertyOrField(p, "ID"), Expression.Constant(123)),
Expression.Equal(Expression.PropertyOrField(p, "PetName"), Expression.Constant("Jim"))
);
var filtered = ApplyPredicate(query, body, p);
使用:
private static Type GetQueryType<T>(IQueryable<T> query) => typeof(T);
private static IQueryable<T> ApplyPredicate<T>(IQueryable<T> query,
Expression body, params ParameterExpression[] parameters)
=> query.Where(Expression.Lambda<Func<T, bool>>(body, parameters));
附加:如果您不喜欢成员名称的字符串文字:
var example = Example(query);
var p = Expression.Parameter(GetQueryType(query));
var body = Expression.And(
Expression.Equal(Expression.PropertyOrField(p, nameof(example.ID)),
Expression.Constant(123)),
Expression.Equal(Expression.PropertyOrField(p, nameof(example.PetName)),
Expression.Constant("Jim"))
);
使用:
private static T Example<T>(IQueryable<T> query) => default(T);