Let's say I've created some anonymous type via a LINQ query:
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
};
Now I want to apply some complex expressions to the query by dynamically building a predicate. What I have been doing for known types is something like this:
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);
Is it possible to do something similar with an anonymous type? I haven't found a way of using anonymous types with PredicateBuilder
and I'm not familiar with any other constructs which would let me dynamically build an expression like this. If not, is there another approach I should take when dynamically generating nested expressions for a query?
Typically, when you use an anonymous type to initialize a variable, you declare the variable as an implicitly typed local variable by using var. The type name cannot be specified in the variable declaration because only the compiler has access to the underlying name of the anonymous type.
In C#, you are allowed to create an anonymous type object with a new keyword without its class definition and var is used to hold the reference of the anonymous types.
An anonymous type is a temporary data type that is inferred based on the data that we include in an object initializer. Properties of anonymous types are read-only in nature so cannot change their values. Let us try to change the value of Age.
In C#, an anonymous type is a type (class) without any name that can contain public read-only properties only. It cannot contain other members, such as fields, methods, events, etc. You create an anonymous type using the new operator with an object initializer syntax.
Assuming that all you need is PredicateBuilder.False<T>
and PredicateBuilder.True<T>
for an anonymous type, you can do it like this:
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>();
}
Now you can rewrite your code without the need to use the anonymous type explicitly:
var predicate = MakeFalse(query);
predicate = predicate.Or(v => v.ID > 10000);
query = query.Where(predicate);
The idea is to have the compiler do the type inference for you. Unfortunately, you end up with an unused method parameter.
In the general case, it may be awkward to use PredicateBuilder
here; it may be easier to use the Expression
API directly. You'll need to get the Type
representing the anonymous type, build the expression without a T
, then flip into generic code to build the final lambda. Fortunately, not too tricky:
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);
with:
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));
Additional: if you don't like the string literals for the member names:
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"))
);
with:
private static T Example<T>(IQueryable<T> query) => default(T);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With