Reputation: 28348
I have this POST endpoint in my ASP.NET Core Web API (with Entity Framework):
[HttpPost]
public async Task<ActionResult<Game>> PostGame(Game game) {
...
game.State = new GameState();
game.State.Map.Rows = game.State.Map.Rows.ToList();
...
}
Then I have these two classes:
public class MapRow {
public int Id { get; set; }
public IEnumerable<MapColumn> Columns { get; set; }
public MapRow() { }
public MapRow(IEnumerable<MapColumn> columns) {
Columns = columns;
}
}
public class Map {
public long Id { get; set; }
public IEnumerable<MapRow> Rows { get; set; }
public Map() { }
public Map(IEnumerable<MapRow> rows) {
Rows = rows;
}
}
Which has two Enumerable
s that need to be converted to a List
or Entity Framework will throw an error similar to this:
System.InvalidOperationException: The type of navigation property 'Columns' on the entity type 'MapRow' is 'SelectRangeIterator' which does not implement ICollection. Collection navigation properties must implement ICollection<> of the target type.
I solved this error for my Rows
by calling .ToList()
in the endpoint itself. However since there are multiple endpoints and you can have Enumerable
s that are quite nested it's quickly becoming unmaintainable having to convert every Enumerable
to a List
manually.
Is there an easier way that would allow me to automate this?
Upvotes: 0
Views: 338
Reputation: 34783
For EF entities, navigation properties for collections should be declared as ICollection<T>
rather than IEnumerable<T>
. So for instance:
public class MapRow {
public int Id { get; set; }
public virtual ICollection<MapColumn> Columns { get; set; } = new List<MapColumn>();
public MapRow() { }
public MapRow(IEnumerable<MapColumn> columns) {
Columns = columns;
}
}
public class Map {
public long Id { get; set; }
public virtual IColleciton<MapRow> Rows { get; set; } = new List<MapRow>();
public Map() { }
public Map(IEnumerable<MapRow> rows) {
Rows = rows;
}
}
ICollection<T>
extends IEnumerable<T>
so any method expecting IEnumerable will be perfectly content with ICollection. It's a different story when you try passing an IEnumerable to something expecting IList or ICollection.
Declaring them as virtual
also allows for EF to assign proxies to these properties to assist with lazy loading if they are needed and the DbContext is still available. It also helps to auto-initialize the properties to a new collection so that when new-ing up a Map, you can immediately start adding Rows and Columns without risking NullReferenceException
s.
Upvotes: 1