How to automatically convert all Enumerable to List when using Entity Framework?

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 Enumerables 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 Enumerables 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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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 NullReferenceExceptions.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x