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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With