Why use ICollection and not IEnumerable or List T 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
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.
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.
ICollection T Vs List T 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.)
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 aforeach
. Which means you still can add filters, like aWhere()
. - 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.
EF Core - Unable to determine the relationship represented by navigation (One-to-Many)
If you specify the [ForeignKey]
attribute, you need to do one of two things:
- Add the attribute to the scalar property, and use the name of the navigation property; or
- Add the attribute to the navigation property, and use the name of the scalar property.
So either:
[ForeignKey("Insurance")]
public int InsuranceId { get; set; }
public Insurance Insurance { get; set; }
or:
public int InsuranceId { get; set; }
[ForeignKey("InsuranceId")]
public Insurance Insurance { get; set; }
By putting the attribute on the scalar property and specifying the name of the scalar property, EF can't understand what you're trying to do.
This applies to all of your [ForeignKey("...")]
attributes in the code.
NB: Since there is only one navigation property to each given entity type, and the scalar property names match the navigation property names with the Id
suffix added, the [ForeignKey]
attributes aren't actually required.
EDIT:
You have two navigation properties for the Insurance
entity:
public Insurance Insurance { get; set; }
...
public Insurance Invoice { get; set; }
I suspect the second one should be:
public Invoice Invoice { get; set; }
Related Topics
Asp.Net: Invalid Postback or Callback Argument
How to Calculate the Angle Between a Line and the Horizontal Axis
Is There a Downside to Adding an Anonymous Empty Delegate on Event Declaration
How to Serialize a C# Anonymous Type to a JSON String
C# Using Reflection to Copy Base Class Properties
C#: Printing All Properties of an Object
How to Replace Multiple White Spaces with One White Space
How to Log into a Site with Webclient
Reference Assignment Is Atomic So Why Is Interlocked.Exchange(Ref Object, Object) Needed
System.Outofmemoryexception' Was Thrown When There Is Still Plenty of Memory Free
How to Get the Propertyinfo of a Specific Property
Initializing C# Auto-Properties
Operator Overloading with C# Extension Methods
How to Update a Table Using Oledb Parameters
Why Firefox Requires Geckodriver
Windows Application Startup Error Exception Code: 0Xe0434352