0

Consider this model and viewmodel:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

public class PersonViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

A form to edit just the e-mail:

<form asp-action="UpdateEmail">
    <input type="hidden" asp-for="Id" />
    <label asp-for="Email"></label>
    <input asp-for="Email" />
    <button type="submit">Update e-mail address</button>
</form>

And the UpdateEmail() POST controller method:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UpdateEmail(int id, [Bind("Id,Email")] Person person)
{
    if (id != person.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            db.Update(person);
            await db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            // ... stuff
        }
        return RedirectToAction("Details", new { id = person.Id });
    }
    return View(auto.Map<PersonViewModel>(person));
}

It is my experience that in this case, all other properties besides Email will be overwritten by NULL values. Do I really have to add them as hidden fields in the form? If so, then what is the point of using [Bind("Id,Email")]?

Stian
  • 1,522
  • 2
  • 22
  • 52
  • All other fields in person will have the default values. What kind of magic are you expecting to know if the default value of a property is a changed value and which one is an unchanged and should not be persisted? – Sir Rufo Apr 18 '20 at 04:12
  • @SirRufo I was hoping it would be possible to keep the values in the database for all other properties than the edited one. – Stian Apr 18 '20 at 04:14
  • How should the db context know that the null value for phone property is a flag. for not to change that property in this case but in any other cases it should change it to null? – Sir Rufo Apr 18 '20 at 04:20
  • @SirRufo I thought that was the purpose of `[Bind("PropertyName")]` - that the context would only replace the values of the specified properties. – Stian Apr 18 '20 at 04:25
  • But that has **nothing** to do with the db context or EF at all. – Sir Rufo Apr 18 '20 at 04:27
  • Ok... I see. Then I guess I would have to load the data from the db and replace the value for the edited property before updating it. – Stian Apr 18 '20 at 04:49

1 Answers1

1

Your form contains only the field names Id and Email, so only these values will be send back to the webserver (and action method). The ASP.NET MVC mechanic can fill these values in the object/class you provided, in this case a Person object, but all the other values stays undefined and have their default values (like null, 0, etc.).

You can change the UpdateEmail() method signature to save the form data in separated arguments like:

public async Task<IActionResult> UpdateEmail(int id, string email)

From there you can load the entity from the database and change the value (or doing it directly).

If you want to work with one object instead of having multiple parameters for each form element, you can define a new model and use it instead:

public class UpdateEmailModel
{
    public int Id { get; set; }
    public string Email { get; set; }    
}

Then you can change the method to:

public async Task<IActionResult> UpdateEmail(UpdateEmailModel model)

Or you can change the type to PersonViewModel since that is the model you are sending with return View(auto.Map<PersonViewModel>(person)); anyway. But this is somehow a little "overkill" because you are sending more data to the html form than using it, but it might be okay depending on what else you are doing with the "unused" data. Maybe you should change the code then to the new model by using return View(auto.Map<UpdateEmailModel>(person));.

As an suggestion, you should not use the Person entity as the receiving class/object for the form data. This way the columns in the database dictate how the HTML forms must be named. This also means that for some reason the HTML view part has some kind of dependency on the database where the data is stored, which it shouldn't. It should only work with the models defined in the controller assembly. But this is only a suggestion and obviously opinion based.

Progman
  • 16,827
  • 6
  • 33
  • 48