56

so, my EF model has relationships and according to what I have seen in examples, those relationships should be done with virtual properties of ICollection.

Example:

 public class Task
    {
        public int Id { get; set; }
        public string Description { get; set; }
        public virtual ICollection<SubTask>  { get; set; }
    }

I read somewhere that I should use IEnumerable to prevent deferred execution, is that correct? It means that if my DAL methods return IEnumerable, still of IQueryable, the SQL will be executed at that moment, and not at the moment when I call .TOList in the web page.

So, what is the best practice? What should I return? IEnumerable, List?, IList, ICollection?

thx

Matt
  • 4,462
  • 5
  • 25
  • 35
Luis Valencia
  • 32,619
  • 93
  • 286
  • 506

2 Answers2

75

IQueryable:

  • Query isn't executed until you really iterate over the items, maybe by doing a .ToList() or a foreach. Which means you still can add filters, like a Where().
  • Extends IEnumerable

IEnumerable:

  • Forward-only list of items. You can't get at "item 4" without passing items 0-3.
  • Read-only list, you can't add to it or remove from it.
  • Still might use deferred execution (IQueryable is still an IEnumerable).

IList:

  • Random access to the full list
  • Probably entirely in memory (no deferred execution, but who knows what the exact class does that implements this?)
  • Supports adding and removing
  • Extends IEnumerable and ICollection

ICollection:

  • Is between IEnumerable and IList.
  • Extends IEnumerable

What is "best" depends on your requirements. Usually though an IEnumerable is "good enough" if you only want to display items. At least always use the generic variant.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
  • 4
    If you expose your navigation properties as IEnumerable, you won't be able to add items to the collection. – Kristof Claes Jul 03 '12 at 08:50
  • 4
    Good break down. As for the defered execution, the query won't be formed until you call .GetEnumerator on the IEnumerable (which the other types all derive from). If you use anything other than IQueryable, all of the results will come to the client and be filtered there rather than filters being applied at the database level. You need IQueryable to retain the expression tree that GetEnumerator will use to generate the appropriate TSQL. – Jim Wooley Jul 03 '12 at 15:13
  • 1
    How about performance hit when using `IEnumerable`? Once type is treated as `IEnumerable` all optimizations LINQ has for lists and arrays are no longer applicable and we are now talking about delegates, etc. – Schultz9999 Feb 03 '15 at 18:31
  • 2
    @Schultz9999 - Most LINQ-to-Objects methods work on IEnumerables, not ILists. They may check internally whether it's also an IList or ICollection and use optimized code for that. See also Jon Skeet's [EduLinq](http://codeblog.jonskeet.uk/2010/12/26/reimplementing-linq-to-objects-part-7-count-and-longcount/) series. – Hans Kesting Feb 04 '15 at 09:39
  • I'd argue that IList cannot be guaranteed to be in memory as it is not a concrete type. – Gusdor Apr 22 '15 at 10:20
  • Whats the use case for having IQueryable on an Entity? – Wouter Nov 26 '20 at 19:50
3

Another difference in case of EF is that the IEnumerable is not executing the query to the DB untill you really enumerate the list. While the IList will execute the query and fetch the items in memory. I had a strange behaviour with caching. Chaching the IEnumarable didn't store the items but rather the unfetched enumeration, everytime i would call the cache and fetch the enumeration for some purpose, it would go to the DB and execute the query, so caching was useless. But a ToList() on the enumeration fetched the items and stored the items in the cache, so only 1 trip to the DB for this. Find more in this post: http://www.dailycode.info/BlogV2/post/entityframework-adding-ienumerable-to-cache

Mark
  • 379
  • 3
  • 7