Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Code First 4.3 / LINQKit predicate for related table

I am using Entity Framework 4.3.1 with a Code First approach. Also, I am using LinqKit so as to use the PredicateBuilder.

If I have tables like so:

Location, TimeZone (Many:1)

..and I wish to have something like so:

Expression<Func<TimeZone, bool>> pred = PredicateBuilder.True<TimeZone>();
pred = pred.And(tz => tz.TimeZoneCode == "EST");

List<Location> locations = context.Locations
    .AsExpandable().Where(pred)
    .Select(loc => loc).ToList();

This does not work, because the predicate is built to accept a TimeZone, but the Where() method receives a Location.

I can rewrite the predicate like so, but I do not want to, because I want to have a predicate factory that creates predicates for specific types (I do not want to use the Navigator properties in this manner):

Expression<Func<Location, bool>> pred = PredicateBuilder.True<Location>();
pred = pred.And(loc => loc.TimeZone.TimeZoneCode == "EST");

What syntax could I use (if any) to use the predicate as constructed in the first example, where it takes a TimeZone, rather than have it take a Location and walk the tree via the navigation properties (as this is less reusable). It would be nice if there was a way to leverage the knowledge that EF has about the navigation properties in the first place, and be able to use a predicate scoped to the type of the navigation property.

like image 774
Pittsburgh DBA Avatar asked Jul 17 '12 19:07

Pittsburgh DBA


1 Answers

After about a week's worth of struggling, I found out that you can in fact do this. The steps are:

  1. Define a Predicate that is the query for your inner property (subPredicate)
  2. Invoke that subPredicate from within another Predicate (predicate), against the property of the parent object.
  3. Expand your predicate when using it in your Where clause.

Here's the revised code for your example:

var subPredicate = PredicateBuilder.True<TimeZone>();
subPredicate = subPredicate.And(tz => tz.TimeZoneCode == "EST");

var predicate = PredicateBuilder.True<Location>();
predicate = predicate.And(l => subPredicate.Invoke(l.TimeZone));

List<Location> locations = context.Locations
    .AsExpandable().Where(pred.Expand())
    .Select(loc => loc).ToList();
like image 92
Grinn Avatar answered Oct 29 '22 20:10

Grinn