0

I got this PATCH endpoint in my C# ASP.NET Core Web API that uses Entity Framework to store my games:

[HttpPatch]
public async Task<ActionResult<Game>> GetGame(long id) {

  var game = await _context.Games.FindAsync(id);

  if (game == null) {
    return NotFound();
  }

  game.State.Map = MapUtils.generateMap();

  _context.Entry(game).State = EntityState.Modified;

  try {
    await _context.SaveChangesAsync();
  }
  catch (DbUpdateConcurrencyException) {
    if (!GameUtils.GameExists(_context, id)) {
      return NotFound();
    }
    else {
      throw;
    }
  }

  return game;
}

When this function is called I get the updated game with Map being set to a List of rows and columns as the response. But then when I use my GET endpoint to retrieve the updated game at a later stage, map is null which means that my entry couldn't have been updated when calling the PATCH.

Here is the GET for retrieving a single game:

[HttpGet("{id}")]
public async Task<ActionResult<Game>> GetGame(long id) {
  var game = await _context.Games.FindAsync(id);

  if (game == null) {
    return NotFound();
  }

  return game;
}

I've tried some solutions from this question but I get the same result.

What am I doing wrong?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Chrillewoodz
  • 27,055
  • 21
  • 92
  • 175

1 Answers1

2
_context.Entry(game).State = EntityState.Modified;

will mark only the game entity as modified, not it's related entities.

Use -

_context.Games.Update(game);

instead to include the related entities.

Also, take a look at this documentation to understand what this method tries to do behind the scene. Specifically, this part -

"For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state."

In short, if the related entities don't have primary keys set, EF will try to Add the instead of Updating.

Finally, both in your GET and PATCH method include Map in the query -

var game = await _context.Games.Include(p => p.State).ThenInclude(p => p.Map).FirstOrDefaultAsync(p => p.Id == id);

Take a look at Loading Related Data

atiyar
  • 7,762
  • 6
  • 34
  • 75
  • Still getting the same result :| – Chrillewoodz Sep 06 '20 at 09:15
  • Seemed promising but `Map` is still null in the response... I get the `State` regardless of if I use the second part of your answer with the `.Include`. – Chrillewoodz Sep 06 '20 at 09:40
  • Could you please share your model classes? And did you update the query in your `PATCH` method too? – atiyar Sep 06 '20 at 09:42
  • Ok now that I added it to PATCH as well I get the `Map`.. but its `Rows` and `Columns` is still not there. `Rows` and `Columns` in the `Map` are `List`s, so how to include all of those as well? Is there not a method which includes all entries so you don't have to do this in every endpoint? – Chrillewoodz Sep 06 '20 at 09:44
  • Used more .ThenInclude for Rows and Columns, but jesus this will bloat my codebase. I should probably move this to some sort of utils class no? – Chrillewoodz Sep 06 '20 at 09:48
  • Doing database queries in your controller action methods is not the best practice anyway. You should have a Service/Repository layer for that purpose. – atiyar Sep 06 '20 at 09:50