In an app the uses EF Core, I am attempting to eliminate duplication of query code by creating a reusable library of predicate expressions. However, I am struggling with predicates that accept runtime parameters.
Let's assume 2 simple entity classes in a parent child relationship:
public class Parent
{
public double Salary { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public double Salary { get; set; }
}
I can retrieve all the parents with a child that earns more than them, using a conventional EF Core query like this:
return Context.Set<Parent>()
.Where(parent => parent.Children.Any(child =>
child.Salary > parent.Salary));
If I want to create a reusable predicate for the above query, I imagine it might look something like this:
private Expression<Func<Child, Parent, bool>> EarnsMoreThanParent = (Child child, Parent parent) => child.Salary > parent.Salary;
The above predicate compiles OK, but I haven't found any way to consume it. The problem is how to pass the parent entity into it at runtime. This doesn't compile:
return _context.Set<Parent>()
.Where(parent => parent.Children.Any(child =>
EarnsMoreThanParent(child, parent));
I understand that the I am conflating the Expression<>
and Func<>
syntax, but I assume that this is a relatively common requirement that must be possible.
Thanks
You can try to wrap the whole expression like this
private Expression<Func<Parent, bool>> EarnsMoreThanParent =
(Parent parent) => parent.Children.Any(child => child.Salary > parent.Salary)
and then use for the whole Where
return _context.Set<Parent>().Where(EarnsMoreThanParent);
The problem with your code is that in .Where(parent => parent.Children.Any(child => EarnsMoreThanParent(child, parent))
the whole expression inside is an expression. So your method EarnsMoreThanParent
won't be called as it's part of Where
expression and EF will try to parse it as expression and would fail.
But yeah, if you need to combine a couple of such condition with different OR
it might not work as it would be like Where(EanrsMoreThanParent).Where(SomeOtherCondition)
and all of them will be translated to AND