Given a primitive value age
I know how to create an expression like this:
//assuming: age is an int or some other primitive type
employee => employee.Age == age
By doing this:
var entityType = typeof(Employee);
var propertyName = "Age";
int age = 30;
var parameter = Expression.Parameter(entityType, "entity");
var lambda = Expression.Lambda(
Expression.Equal(
Expression.Property(parameter, propertyName),
Expression.Constant(age)
)
, parameter);
That works fine except in scenarios where the property and constant in question are not primitive types.
How would I construct a similar expression if the comparison is between objects?
With EF I can just write:
Location location = GetCurrentLocation();
employees = DataContext.Employees.Where(e => e.Location == location);
That also works, but if I try to create the same expression:
var entityType = typeof(Employee);
var propertyName = "Location";
var location = GetCurrentLocation();
var parameter = Expression.Parameter(entityType, "entity");
var lambda = Expression.Lambda(
Expression.Equal(
Expression.Property(parameter, propertyName),
Expression.Constant(location)
)
, parameter);
I get an error that says:
Unable to create a constant value of type 'Location'. Only primitive types or enumeration types are supported in this context.
My suspicion is that Expression.Constant()
only expects primitive types, so I need to use a different expression factory method. (maype Expression.Object
? - I know that doesn't exist)
Is there a way to create an expression that compares objects? Why is that EF is able to interpret it correctly if its a compiled LINQ statement, but not when it is an expression?
You can't do that because EF doesn't know how to translate equality comparisons on
Location
into a SQL expression.However, if you know what properties of
Location
you want to compare, you can do this with anonymous types:Of course that's equivalent to:
Give the code below a run. I wanted to test your assumption that e => e.Location == location is compiling into something that can be constructed with Expression.Equal, Expression.Property, and Expression.Constant.
It seems like it isn't, and its because the right expression isn't a Constant. To see this, uncomment the commented out code.
What I don't understand is why the right expression is a MemberExpression. Perhaps someone who knows the linq expression compiler can shed more light onto this then I can.
Edit: This may have to do with closure in lambdas - a class is created behind the scenes which contains the closed over variables. The location might then be a member of that class. I'm not sure about this, but it's what I suspect.
This post may shed additional light on the situation.
In addition to what has been mentioned in previous answers. A more specific solution would go as such:
IMPORTANT POINTS :
propertyType
andvalueType
are compatible (either they are the same type or are convertible)KeyAttribute
)Hope that helps.