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 => empl
In addition to what has been mentioned in previous answers. A more specific solution would go as such:
public static Expression CreateExpression<T>(string propertyName, object valueToCompare)
{
// get the type of entity
var entityType = typeof(T);
// get the type of the value object
var valueType = valueToCompare.GetType();
var entityProperty = entityType.GetProperty(propertyName);
var propertyType = entityProperty.PropertyType;
// Expression: "entity"
var parameter = Expression.Parameter(entityType, "entity");
// check if the property type is a value type
// only value types work
if (propertyType.IsValueType || propertyType.Equals(typeof(string)))
{
// Expression: entity.Property == value
return Expression.Equal(
Expression.Property(parameter, entityProperty),
Expression.Constant(valueToCompare)
);
}
// if not, then use the key
else
{
// get the key property
var keyProperty = propertyType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0);
// Expression: entity.Property.Key == value.Key
return Expression.Equal(
Expression.Property(
Expression.Property(parameter, entityProperty),
keyProperty
),
Expression.Constant(
keyProperty.GetValue(valueToCompare),
keyProperty.PropertyType
)
);
}
}
IMPORTANT POINTS :
propertyType
and valueType
are compatible (either they are the same type or are convertible)KeyAttribute
)Hope that helps.
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.
class Program {
static void Main(string[] args) {
var location = new Location();
Expression<Func<Employee, bool>> expression = e => e.Location == location;
var untypedBody = expression.Body;
//The untyped body is a BinaryExpression
Debug.Assert(
typeof(BinaryExpression).IsAssignableFrom(untypedBody.GetType()),
"Not Expression.Equal");
var body = (BinaryExpression)untypedBody;
var untypedLeft = body.Left;
var untypedRight = body.Right;
//The untyped left expression is a MemberExpression
Debug.Assert(
typeof(MemberExpression).IsAssignableFrom(untypedLeft.GetType()),
"Not Expression.Property");
////The untyped right expression is a ConstantExpression
//Debug.Assert(
// typeof(ConstantExpression).IsAssignableFrom(untypedRight.GetType()),
// "Not Expression.Constant");
//The untyped right expression is a MemberExpression?
Debug.Assert(
typeof(MemberExpression).IsAssignableFrom(untypedRight.GetType())));
}
}
public class Employee
{
public Location Location { get; set; }
}
public class Location { }
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.
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:
var location = GetCurrentLocation();
var locationObj = new { location.LocationName, location.LocationDescription };
employees = DataContext.Employees.Where(e => new { e.Location.LocationName, e.Location.Description } == locationObj);
Of course that's equivalent to:
var location = GetCurrentLocation();
employees = DataContext.Employees.Where(e => e.Location.LocationName == location.Name &&
e.Location.Description == location.Description);