5

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.

Pittsburgh DBA
  • 6,672
  • 2
  • 39
  • 68

2 Answers2

6

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();
Grinn
  • 5,370
  • 38
  • 51
  • thanks for the update! Can you add insight into whether or not this predicate is applied server-side, via LINQ/LINQKit feeding EF something it can translate to SQL, or is this applied brute force internally in EF on an unfiltered dataset? – Pittsburgh DBA Jan 27 '15 at 20:25
  • @PittsburghDBA this is resulting in a call to the DB via EF, translating into SQL. – Grinn Jan 27 '15 at 21:40
  • @PittsburghDBA I think I can live with that. Thanks! – Grinn Jan 29 '15 at 19:11
2

Just to update this: it turns out that the intent of these kinds of predicates is to filter the primary entity. The mental concept is: decide which entities you would like to return, and return them. EF is apparently not designed for this kind of deep predicate application on child entities.

One person (I don't remember where) made a great point: If the children were pre-loaded, you would not expect a partially-loaded collection. Thus, it would not make sense to have an Invoice entity, for example, but only half of the Invoice Lines.

What I wanted to achieve was more along the lines of EXISTS() or IN(), where you could say "Give me all Invoices where there are Invoice Lines for Product Types 'Nuts' and 'Bolts'". This is feasible, but you may have to apply LINQ or compose your own object. The intent of EF out of the box is to hand you the Invoice, and then you may lazily or eagerly load the Invoice Lines, but not using them as a filter straight from the database.

There are some constructs that I saw to accomplish something close, but the syntax gets very unmanageable very quickly.

Don't fight city hall.

Pittsburgh DBA
  • 6,672
  • 2
  • 39
  • 68
  • I'm in this scenario... after plenty of reading and trial and error coding I've also learn't don't fight the system, else you get complex and ugly code. Ugh!!! EF & PredicateBuilder is/are good for primary entities 'works'. What did you end up doing regarding nested entities? I'm at a crossroads now, deciding what to do next. (I realise it's been 2 yrs since your answer). – OpcodePete Oct 23 '14 at 05:10
  • We abandoned EF and now use SQLClient. None of these ORM platforms can compete with ADO.Net in terms of raw performance, never mind these predicate issues. – Pittsburgh DBA Oct 24 '14 at 16:16
  • @ThomasVeil take a look at the brutal differences outlined here: https://web.archive.org/web/20131205132904/http://ormbattle.net/ – Pittsburgh DBA Oct 24 '14 at 16:22
  • Agreed, ADO.NET is def'n king of the hill. Thank you very much, hadn't considered this. Something I will now consider. Pity I have spent so much time and energy invested in EF. The question I am now asking myself is... is EF production ready in terms of performance. – OpcodePete Oct 24 '14 at 21:29
  • Some food for thought... http://stackoverflow.com/questions/19311491/how-entity-framework-works-for-large-number-of-records – OpcodePete Oct 24 '14 at 22:26