Error adding an owned entity to the parent entity using ASP.NET Core 6 Web API

33 Views Asked by At

I am building a habit tracking application in Blazor with an ASP.NET Core 6 Web API for backend which uses Entity Framework. I have a Habit entity which owns many Implementation entities like this:

modelBuilder.Entity<Habit>().OwnsMany(h => h.Implementations);

I am trying to add an implementation to a habit which already has an existing implementation. This is the code within my controller action method:

public async Task<IActionResult> AddHabitReview([FromBody] HabitReviewModel model)
{
    try
    {
        var habit = await _context.Habits.FindAsync(model.Id);

        if (habit == null)
            return NotFound("No habit found");

        habit.AddImplementation(/*implementation parameters*/);

        _context.Habits.Update(habit);

        await _context.SaveChangesAsync();

        return Ok();
    }
    catch (Exception ex)
    {
        var message = ex.Message;
        return BadRequest(message);
    }
}

but I am getting the following exception:

The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

I also checked the DB query via SQL Server profiler. For some reason, it is creating an UPDATE command for Implementation table with Id as my new implementation ID. I expected it to generate a CREATE command for Implementation table, since I was creating a new implementation and attaching it to the habit entity.

Can someone point out what I am doing wrong?

1

There are 1 best solutions below

1
Jason Pan On

The root of the error is that EF Core tried to update the Habit entity, but did not properly handle the changes to the Implementation collection.

So the correct usage like below:

    var habit = await _context.Habits.FindAsync(model.Id);

    if (habit == null)
        return NotFound("No habit found");

    // create new obj
    var newImplementation = new Implementation(...);

    // Add new Implementation instance in `habit.Implementations`
    habit.Implementations.Add(newImplementation);

    // Since we have add new Implementation instance into the Habit collection
    // So no need to explicitly call the Update method
    await _context.SaveChangesAsync();

    return Ok();