I am reading "Entity Framework Core in Action" by Jon P Smith. There it says:
Could you provide some examples when Hashet is a proper collection type? And why?
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
Note: I haven't looked at the speed of creation for any of these - I just do that because the book "Framework Design Guidelines" book shows that as best practice - see page 250.
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
IEnumerable<T> is a special case, because it turns a collection into a read-only version, due to
IEnumerable not having a
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.