使用匿名类型构建表达式
Building an Expression with an anonymous type
假设我通过 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
一起使用的方法,而且我不熟悉任何其他可以让我动态构建这样的表达式的构造。如果不是,在为查询动态生成嵌套表达式时是否应该采用另一种方法?
假设你只需要 PredicateBuilder.False<T>
和 PredicateBuilder.True<T>
匿名类型,你可以这样做:
private static Expression<Func<T,bool>> MakeTrue<T>(IQueryable<T> ignored) {
return PredicateBuilder.True<T>();
}
private static Expression<Func<T,bool>> MakeFalse<T>(IQueryable<T> ignored) {
return PredicateBuilder.False<T>();
}
现在您可以重写代码而无需显式使用匿名类型:
var predicate = MakeFalse(query);
predicate = predicate.Or(v => v.ID > 10000);
query = query.Where(predicate);
我们的想法是让编译器为您进行类型推断。不幸的是,您最终得到了一个未使用的方法参数。
一般情况下,这里用PredicateBuilder
可能比较尴尬;直接使用 Expression
API 可能更容易。您需要获取表示匿名类型的 Type
,构建表达式 without a 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);
假设我通过 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
一起使用的方法,而且我不熟悉任何其他可以让我动态构建这样的表达式的构造。如果不是,在为查询动态生成嵌套表达式时是否应该采用另一种方法?
假设你只需要 PredicateBuilder.False<T>
和 PredicateBuilder.True<T>
匿名类型,你可以这样做:
private static Expression<Func<T,bool>> MakeTrue<T>(IQueryable<T> ignored) {
return PredicateBuilder.True<T>();
}
private static Expression<Func<T,bool>> MakeFalse<T>(IQueryable<T> ignored) {
return PredicateBuilder.False<T>();
}
现在您可以重写代码而无需显式使用匿名类型:
var predicate = MakeFalse(query);
predicate = predicate.Or(v => v.ID > 10000);
query = query.Where(predicate);
我们的想法是让编译器为您进行类型推断。不幸的是,您最终得到了一个未使用的方法参数。
一般情况下,这里用PredicateBuilder
可能比较尴尬;直接使用 Expression
API 可能更容易。您需要获取表示匿名类型的 Type
,构建表达式 without a 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);