First of all, this code works but I don't understand why it works. This question is asking why I can access a property in an expression tree when I don't think I should.
Let's start by establishing context.
Context
I set up two example classes that are included in my dbcontext. They do not have virtual navigation properties. This is a limitation of my legacy dbcontext. The entities in my context are different but I'm using these as an example.
class Person
{
int PersonId {get; set;}
string FirstName {get; set;}
}
class Address
{
int AddressId {get; set;}
string Address {get; set;}
string City {get; set;}
string State {get; set;}
string Zip {get; set;}
int PersonId {get; set;}
}
Let's say that I have a function that returns type Expression<Func<Person, bool>> This function helps build expressions that are used by entity framework.
I need to build a filter that "joins" address to person using an Any. The predicate version of this would look something like this.
Expression<Func<Person, bool>> myFilter = x => myDbContext.Address.Any(y => y.PersonId == x.PersonId && y.City == "New York");
Since I need to join to various tables that aren't necessarily the Address table, I was able to build this with a generic Expression Tree like this:
Expression<Func<Person, bool>> BuildMyExpression<T>(IQueryable<T> passedInGenericQueryable)
{
// This variable is passed in to the function. It's a generic but I'm showing the concrete type here for this example.
//IQueryable<Address> passedInGenericQueryable;
// This build an expression tree for y.City == "New York" but I omitted the details because my question isn't about this
Expression<Func<T, bool>> compareCity = MagicFunction<T>("New York", "City");
var personParam = Expression.Parameter(typeof(Person), "Person");
var addressParam = Expression.Parameter(typeof(T), "Address"); // I named this address for readability but it could be several different types
// Compares the PersonId on the Person table and any other table that has a PersonId property
var personIdEqual = Expression.Equal(
Expression.Property(personParam, "PersonId"),
Expression.Property(addressParam, "PersonId")
);
// Ands the city comparison and the personId comparison
var andedExpressions = Expression.AndAlso(
personIdEqual,
Expression.Invoke(compareCity, addressParam)
);
var lambdaExpression = Expression.Lambda<Func<T, bool>>(andedExpressions, addressParam);
// Create an expression tree to represent the Any method on IQueryable<Address>
var anyMethod = typeof(Queryable).GetMethods()
.Where(m => m.Name == "Any" && m.GetParameters().Length == 2)
.Single()
.MakeGenericMethod(typeof(T));
var callAnyExpression = Expression.Call(
anyMethod,
Expression.Constant(passedInGenericQueryable),
lambdaExpression
);
Expression<Func<Person, bool>> fullExpression = Expression.Lambda<Func<Person, bool>>(callAnyExpression, personParam);
return fullExpression;
}
Question
My question is specifically about the lines that builds the Any method.
// Create an expression tree to represent the Any method on IQueryable<Address>
var anyMethod = typeof(Queryable).GetMethods()
.Where(m => m.Name == "Any" && m.GetParameters().Length == 2)
.Single()
.MakeGenericMethod(typeof(T));
var callAnyExpression = Expression.Call(
anyMethod,
Expression.Constant(passedInGenericQueryable),
lambdaExpression
);
How are these lines able to access the properties on the Person entity?
If I look at the definition of the Any method it looks like this:
public static bool Any<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
It's an extension method that takes in an Expression<Func<TSource, bool>> and returns a bool When I'm building my expression, I pass in IQueryable<Address> but it accesses a member on Person
E.G.
var personIdEqual = Expression.Equal(
Expression.Property(personParam, "PersonId"),
Expression.Property(addressParam, "PersonId")
);
If the collection it's operating on is IQueryable<TSource> (Address in this case), how is it able to access the PersonId property on Person?
My question is very similar to this question but not quite the same.