EF Core - Update fields before SaveChanges()/SaveChangesAsync()

Viewed 804

I'm just learning EF Core and .NET Core MVC in general, so my apologies if this question has been covered before - I've searched StackOverflow and Microsoft Docs and come up with nothing.

A simplified version of my model:

    public class MyModel
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public MyModel Parent { get; set; }
        public string Type { get; set; }
        public string Subtype { get; set; }
    }

My controller:

    public class MyController : Controller
    {
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit (int? id )
        {
            if ( id == null ) return NotFound();
            var modelToUpdate = await _context.MyModels
                .Include( m => m.Parent )
                .FirstOrDefaultAsync( m => m.Id == id );
            if ( modelToUpdate == null ) return NotFound();

            if ( string.IsNullOrWhitespace(modelToUpdate.Type) )
            {
                modelToUpdate.Type = modelToUpdate.Parent.Type;
            }

            if ( await TryUpdateModelAsync<MyModel>(
                    modelToUpdate,
                    "",
                    m => m.Id, m => m.ParentId, m => m.Type, m => m.Subtype
                ) 
            ) {
                try
                {
                    await _context.SaveChangesAsync();
                }
                catch ( DbUpdateException )
                {
                    ModelState.AddModelError( "", "Unable to save changes" );
                }
                return RedirectToAction("Index");
            }
            return View(modelToUpdate);
        }
    }

Setting the modelToUpdate.Type to modelToUpdate.Parent.Type is not being written to the database. As to why I'm doing this: to improve consistency in the data set. I could be doing that client-side with AJAX, but I'd rather not.

EF is recognising the Entity is changed, but doesn't update the database row with the latest value in the Controller.

I've looked at the _context.ChangeTracker and tried manually setting:

    _context.Entity(modelToUpdate).State = EntityState.Modified;

and with:

    _context.Entity(modelToUpdate).Property( m => m.Type ).CurrentValue = modelToUpdate.Type;

and .OriginalValue, to no effect.

When I set a breakpoint on the TryUpdateModelAsync() statement, modelToUpdate is showing the correct values. A breakpoint at _context.SaveChangesAsync(); instead shows null.

It looks like TryUpdateModelAsync() is using the original values returned from the View and completely ignores the values I set in the Controller when post-processing.

As an added note: I have similar logic for the [HttpPost("Create")], and that works flawlessly - but I'm using the following there:

    if ( ModelState.IsValid )
    {
        _context.Add(model);
        await _context.SaveChangesAsync();
    }

Can someone explain what I'm missing about state tracking here?

0 Answers
Related