2

In EF, when you want to include a navigation property in the result of a query, you use Include(). Since some queries require calling this more than once, I tried to create a generic wrapper around this concept:

public IQueryable<T> FindAll<P>(params Expression<Func<T, P>>[] predicates) where P : class {
  var entities = AllEntities();
  foreach (var p in predicates) entities = entities.Include(p);
  return entities;
}

And call it as such:

var customers = FindAll(q => q.Orders, q => q.Invoices, q => q.Contacts);

Questions:

  1. The function compiles, but when I call it: "The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly." What am I doing wrong?
  2. If I get it to work, can I call Include() the first time: var customers = FindAll(q => q.Orders, q => q.Invoices); and then again later in a separate step: customers = customers.Include(p => p.Invoices); or will that result in poor performance such that I should do the includes in one go?

EDIT:
JonSkeet's answer is correct, of course, and yet there is this solution which seems to do what I want. Not sure why it works however. Is it because of the Aggregate() function?

Community
  • 1
  • 1
Bobby B
  • 2,287
  • 2
  • 24
  • 47

1 Answers1

4

Fundamentally, I think you've got a problem - assuming that q.Orders, q.Invoices and q.Contacts return different types, you can't express what you want to happen. You probably want something like:

entities.Include<Parent, Order>(p => p.Order);
entities.Include<Parent, Invoice>(p => p.Invoices);
entities.Include<Parent, Contact>(p => p.Contacts);

... so you want a different value for P for each predicate. But you're calling a single method, providing a single type argument for P.

It's not clear that this is really giving you much benefit anyway... couldn't you just write:

var customers = AllEntities().Include(q => q.Orders)
                             .Include(q => q.Invoices)
                             .Include(q => q.Contacts);

I'd say that's clearer and it gets round the "multiple type parameters" problem.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes that's what I'm doing now, but its in a generic repository and I wanted to be able to specify which of those navigation properties to pull in together with a query; and of course different entities have different navigation properties. If there's no good way to do it I'll carry on using the normal approach. – Bobby B Oct 24 '12 at 11:53
  • @BobbyB: Do you understand may point about why your current approach can't work? What type argument would you *want* to use for `P`? – Jon Skeet Oct 24 '12 at 12:01
  • yes what I meant is that I'm currently doing it using your "alternative way" (in your second code block). The way I *wanted* to do it doesn't work for the exact reason you explained - `P` is multiple types i.e. `Orders`, `Invoices` and `Contacts`. – Bobby B Oct 24 '12 at 12:03
  • I agree with your comments, but how does it compare [to this answer](http://stackoverflow.com/a/5376637/1711500) which seems to do what I want? Not sure of the difference here? – Bobby B Oct 24 '12 at 16:51
  • @BobbyB: The difference is that that question only took `Expression>` expressions. I'm somewhat surprised it works at all, but if it helps you, that's fine. It doesn't change my point that the reason your version *isn't* working is that you're trying to take multiple `Func` projections (not really predicates, btw) but with different `P` types, all in one call. – Jon Skeet Oct 24 '12 at 17:33
  • I'm also using expressions... see question's code. But you're right I was using `P` instead of `object`. So does *that* approach work because of the `object` type, or because of the use of `Aggregate()`? – Bobby B Oct 24 '12 at 18:11
  • @BobbyB: It works because all of the expressions can be converted to `Func`, but there's no single `P` (other than `object` or perhaps some interface or base class) such that all of your expressions can be converted to `Func`. It's not clear to me whether the `Aggregate` call in the other post was actually required - I suspect it was just a fancy way of doing a `foreach` loop really :) – Jon Skeet Oct 24 '12 at 20:55