Icollection<T> VS List<T> in Entity Framework

ICollectionT Vs ListT in Entity Framework

Entity Framework would use ICollection<T> because it needs to support Add operations, which are not part of the IEnumerable<T> interface.

Also note that you were using ICollection<T>, you were merely exposing it as the List<T> implementation. List<T> brings along with it IList<T>, ICollection<T>, and IEnumerable<T>.

As for your change, exposing via the interface is a good choice, despite List<T> working. The interface defines the contract but not the implementation. The implementation could change. In some instances, perhaps the implementation could be a HashSet<T>, for example. (This is a mindset you could use for more than just Entity Framework, by the way. A good object-oriented practice is to program towards the interface and not the implementation. Implementations can and will change.)

Why use ICollection and not IEnumerable or ListT on many-many/one-many relationships?

Usually what you choose will depend on which methods you need access to. In general - IEnumerable<> (MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx) for a list of objects that only needs to be iterated through, ICollection<> (MSDN: http://msdn.microsoft.com/en-us/library/92t2ye13.aspx) for a list of objects that needs to be iterated through and modified, List<> for a list of objects that needs to be iterated through, modified, sorted, etc (See here for a full list: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx).

From a more specific standpoint, lazy loading comes in to play with choosing the type. By default, navigation properties in Entity Framework come with change tracking and are proxies. In order for the dynamic proxy to be created as a navigation property, the virtual type must implement ICollection.

A navigation property that represents the "many" end of a relationship must return a type that implements ICollection, where T is the type of the object at the other end of the relationship. -Requirements for Creating POCO ProxiesMSDN

More information on Defining and Managing RelationshipsMSDN

Collection vs List which I have to use

There appear to be no docs for this, but Entity Framework supports any IEnumerable<T> for collection navigation properties.

So you can just declare them as:

  • IEnumerable<T>
  • ICollection<T>
  • Collection<T>
  • HashSet<T>
  • IList<T>
  • List<T>

And probably more. As far as Entity Framework is concerned, there's no difference, it'll assign the appropriate one at runtime and can do all it does with each one, apart from calling AddRange() on most of them, but when do you want to use that?

If you want databinding, you could use an ObservableCollection<T>, for example, otherwise I'd go with the leanest interface that still allows adding items: ICollection<T>.

What is the difference between List (of T) and Collection(of T)?

Collection<T> is a customizable wrapper around IList<T>. While IList<T> is not sealed, it doesn't provide any customization points. Collection<T>'s methods are by default delegated to the standard IList<T> methods, but can be easily overridden to do what you want. It is also possible to wireup events inside a Collection<T> that I don't believe could be done with an IList.

In short, it's much easier to extend it after the fact, which could potentially mean a lot less refactoring.

ListT is better then ICollectionT? And why implement redundant interfaces?

Q1:

A List<T> might be more 'powerfull' than an ICollection<T>, but it is less abstract.
If ICollection<T> exposes everything you need, then why would you specify that he has to use a List<T> and thereby limit the user of your interface / method / whatever and constrain him that he has to use a List<T>?

The user of your class / interface might not use a List<T> but a HashedSet<T> for instance, which is also an ICollection<T>, but not a List<T>.

If he wants to pass his HashedSet to your method, that would mean he has to convert his set to a list, if you define that your method should accept a List instead of an ICollection<T>.

An ICollection is less specific than a List, which means that as soon as a type implements ICollection, that type can be consumed by your method.
Offcourse you can only do this when an ICollection specifies all the functionality that you need in your method.

If you need to be able to access a certain element in the collection by using an indexer, an ICollection<T> would not be a good choice as an argument type for your interface, since ICollection<T> does not define such an indexer.

Q2 & Q3:

It's all about abstraction and the Liskov substitution principle.

When you have a method which accepts an IEnumerable for instance, you can assign an IReadonlyCollection to it, because it implements IEnumerable next to IEnumerable<T>. If it would only implement IEnumerable<T>, then you would not be able to use assign an IReadOnlyCollection<T> as an argument to a method which accepts an argument of type IEnumerable.

EF ICollection Vs List Vs IEnumerable Vs IQueryable

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.

What's the effect of declaring List instead of ICollection in Code First EF?

Declaring your property as ICollection<T> or List<T> has nothing in common with lazy loading - it's the virtual modifier that controls whether your property can be lazy loaded or not.

The difference is that by declaring the property to be a List<T>, you are forcing the EF to create and populate a concrete class (or class derived from that concrete class) when loading your entity (either lazy or eager). While when declared as ICollection<T>, EF can populate it with some internal data structure, T[], HashSet<T> etc. i.e. whatever class that implements ICollection<T>.

For instance, using your variant with ICollection, try something like this

var result = db.Thong.Include(thong => thong.Things).ToList();

and examine the Things member actual type. You'll see that it's a HashSet<Thing>, not List<Thing>.

EF Core one to many relationships: ICollection or Hashset?

UPDATE: I have just finished updating my book Entity Framework Core in Action and I did some performance tests which shows HashSet<T> is quicker if you are doing a normal query (i.e. without AsNoTracking in the query). That's because EF Core does something called Identity Resolution (read about this in one of my articles), which is slower with non-HashSet collections. So, if you have LOTs of entries in your collections, then HashSet<T> is better.

I personally use ICollection<T> for normal properties, just because its a well-known interface with minimal overheads, i.e. it is very slightly quicker to create an ICollection than a IList. You can of course use HashSet<T>, which is what EF Core uses, but I find HashSet's are a little harder to set than ICollection<T>, which takes a List<T>.

The one place you have to use HashSet<T> if you are using uninitialized backing field collections - see code below:

private HashSet<Review> _reviews;
public IEnumerable<Review> Reviews => _reviews?.ToList();

UPDATE: With EF Core 3 the limitation of having to use HashSet<T> for uninitialized collections has been removed (see this issue). You can use ICollection<T>, List<T> etc.

IEnumerable<T> is a special case, because it turns a collection into a read-only version, due to IEnumerable not having a Add or Remove method. Backing fields plus IEnumerable<T> (see code above) allows you to "lock down" a collection relationship so that it can only be changed from inside the class (see my article Domain-Driven Design in EF Core).

When I use backing field collections I leave them uninitialized, so they need to be HashSet<T>. This allows me to detect when I forgot to use .Include when loading an entity, e.g. if I loaded a book without .Include(p => p.Reviews) and then accessed the Reviews property I would get a null reference exception. This is just a safe way of programming.

If you initialise a backing field collection then it can be ICollection etc., but I don't recommend initialising a backing field collection because it can cause problems if you forget the Include and then add a item to the collection. In that case EF Core deletes any existing reviews and replaces them with the the new one you added. From EF Core's point of view its doing what you said, but its most likely NOT what you intended.

IEnumerableT.Count() vs ListT.Count with Entity Framework

Enumerable.Count<T> (the extension method for IEnumerable<T>) just calls Count if the underlying type is an ICollection<T>, so for List<T> there is no difference.

Queryable.Count<T> (the extension method for IQueryable<T>) will use the underlying query provider, which in many cases will push the count down to the actual SQL, which will perform faster than counting the objects in memory.

If a filter is applied (e.g. Count(i => i.Name = "John")) or if the underlying type is not an ICollection<T>, the collection is enumerated to compute the count.

is one more preferred than the other?

I generally prefer to use Count() since 1) it's more portable (the underlying type can be anything that implements IEnumerable<T> or IQueryable<T>) and 2) it's easier to add a filter later if necessary.

As Tim states in his comment, I also prefer using Any() to Count() > 0 since it doesn't have to actually count the items - it will just check for the existence of one item. Conversely I use !Any() instead of Count() == 0.



Related Topics



Leave a reply



Submit