Reputation: 574
I recently ran into the requirement to implement server side filtering for a Kendo UI grid control. Since the comparison operator(s) in the linq to entities lambda expressions wouldn't be known at compile time, it was necessary to build the expressions dynamically. I've never had the need to do this before, but I did enough digging to figure out how to build simple lambda expressions from expression trees. As an example, I build the expression for a simple filter such as people.Where(person => person.LastName == "Doe")
as follows:
ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression left = Expression.Property(filterParam, "LastName");
ConstantExpression right = Expression.Constant("Doe", typeof(string));
BinaryExpression predicate = Expression.Equal(left, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>(predicate, filterParam);
IQueryable<Person> filtered = people.Where(lambda);
Ok, so on to the question. I encountered the need to do something similar to above except filtering by full name instead of just last name. I don't have a field in my database for full name--just separate first and last name fields, so I can't reference a single property for this. I need to concatenate the two properties in the expression so that the resulting lambda would look like:
people.Where(person => (person.firstName + person.LastName) == "JohnDoe")
I haven't been able to find an example showing how to dynamically build a lambda like that from expression trees. I'm assuming it must be possible--it doesn't seem like that crazy of a scenario, does it?
Edit: I'd also be interested in knowing how to do:
people.Where(person => (person.FirstName + " " + person.LastName) == "John Doe")
Edit2: The solution to this one is a pretty simple change from king king's answer:
ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression first = Expression.Property(filterParam, "FirstName");
MemberExpression last = Expression.Property(filterParam, "LastName");
Type strType = typeof(string);
MethodCallExpression concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { strType, strType, strType }),
first, Expression.Constant(" ", strType), last);
ConstantExpression right = Expression.Constant("John Doe", strType);
BinaryExpression predicate = Expression.Equals(concat, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>>(predicate, filterParam);
IQueryable<Person> filtered = people.Where(lambda);
Upvotes: 1
Views: 1325
Reputation: 9830
To make this easier and reusable, I do define extra properties on the Entity model by using PropertyTranslator in combination with QueryInterceptor.
With this code you can define the FullName property on the Entity model like this:
public class Person
{
private static readonly CompiledExpressionMap<Employee, string> FullNameExpr =
DefaultTranslationOf<Employee>.Property(e => e.FullName).Is(e => e.FirstName + " " + e.LastName);
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public string FullName
{
get
{
return FullNameExpr.Evaluate(this);
}
}
}
And just use this Fullname property in a query like:
IQueryable<Person> filtered = people
.InterceptWith(new PropertyVisitor()).AsQueryable()
.Where(p => p.FullName == "John Doe");
I'm not sure if you can implement this in your design, but if possible investigate this option.
You can check the example project here.
Upvotes: 1
Reputation: 63327
You can try adding a MethodCallExpression
to use the string.Concat
method:
var firstName = Expression.Property(filterParam, "FirstName");
var lastName = Expression.Property(filterParam, "LastName");
var concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { typeof(string), typeof(string) }),
firstName, lastName);
BinaryExpression predicate = Expression.Equal(concat, right);
//....
Upvotes: 2