Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building an Expression with an anonymous type

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?

like image 822
Mike Cluck Avatar asked May 19 '17 16:05

Mike Cluck


People also ask

How do you declare an anonymous type?

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.

How do I make an anonymous object?

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.

What is anonymous type of functions?

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.

What is an anonymous type in C#?

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.


2 Answers

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.

like image 56
Sergey Kalinichenko Avatar answered Oct 18 '22 11:10

Sergey Kalinichenko


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);
like image 4
Marc Gravell Avatar answered Oct 18 '22 10:10

Marc Gravell