I have the edit code calling.
The code in the controller:
[HttpPost]
public IActionResult Edit(CustomerEntries obj)
{
if (ModelState.IsValid)
{
_db.CustomerEntries.Update(obj);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(obj);
}
But it's adding a new row instead of updating the existing row. Has anyone come across this before and does anyone have a clue what I'm doing wrong? Could it be at the database level? I'm using SQL Server database
This usually means that the object to be inserted isn't complete. Check your "obj" instance and you will likely find that the PK field (Id, CustomerId, etc) is not set, instead a default int
/Guid
. With a default/unknown ID EF's Update()
will insert a new row.
This is a common problem with passing entities in MVC/web where the expectation when passing an entity class to serve as the Model will be passed back during a form POST. However, the "entity" coming back from the client is not the same object you might have sent the view, it is a set of values in a JSON object parsed into an entity. The values sent to populate that entity are the values of any <input>
elements in the form. So when you pass an entity to the view you need to add hidden inputs for the ID as well as any values that you might not be actively editing but want preserved.
@Html.HiddenFor(model => model.Id)
This creates a <input type="hidden" ...>
element in the page containing the PK Id so that this can be populated in the data sent back to the POST. This will ensure that the Id/CustomerId etc. are passed back.
However, I strongly recommend avoiding using Update()
to update data from web clients. This approach is prone to tampering as well as bugs where missing fields can lead to data being erased unexpectedly. Browser debug tools are all an attacker/mischief intended individual needs to tamper with data. Update()
will create an SQL UPDATE
statement for every column in the table whether values have changed or not, or whether you expect/intend for them to change or not. It can also lead to data being inserted instead of updated as you have seen. Instead, load the data and copy over just the values you expect a user to change:
[HttpPost]
public IActionResult Edit(CustomerEntries obj)
{
if (!ModelState.IsValid) return View(obj);
var existingCustomer = _db.CustomerEntries.Single(c => c.Id == obj.Id);
existingCustomer.ContactPhone = obj.ContactPhone;
// ... etc. only copy across values you expect to be updated.
_db.SaveChanges();
return RedirectToAction("Index");
}
This ensures that only values you expect to change are changed. The change tracking in EF will only create an UPDATE
statement if values actually change, and only for the values that actually do change. Attackers cannot corrupt data by changing values in the POST JSON payload that you don't copy across.