skip to Main Content

To simplify the problem, in C# I have a model like this

public class Person
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    [MaxLength(128)]
    public string? Id { get; set; }

    [MaxLength(256)]
    public string PersonName { get; set; }

    public short PersonAge { get; set; }

// ommitted other properties

now, I have a form like this in ASP Core in the frontend

@model Person


<form action="/api/new-person">
    <input form="PersonName"/>
    <button type="submit"></button>
</form>

As you can see, I only have PersonName in my form, in my backend I only want to be able to update the Person object in my database for the value of PersonName because it is property that exists in the form, again, this is a simplified example because my object is massive and it changes just about all the time not to mention a lot of foreign keys, now, I don’t want to painstakingly set each properties in the backend such as

var existingPersonFromDatabase = await _appDbContext.User.FirstOrDefaultAsync(x=>x.Id == formPerson.Id);

existingPersonFromDatabase.PersonName = formPerson.PersonName;

existingPersonFromDatabase.PersonAge = formPerson.PersonAge ;
// + settings 20 more properties one by one

so the question here is, how do i make it so that i only have to update the fields existing in the form?

2

Answers


  1. Chosen as BEST ANSWER

    I found the answer, for some reason this is not well documented by Microsoft and its hidden below the docs, I used TryUpdateModelAsync in the Controller,

    inside the controller you can have

    var existingObject = await _appDbContext.Objects.FirstAsync(x => x.Id == 1);
    
    // then we can populate this existing object with the one in the form
    await TryUpdateModelAsync(existingObject);
    

    below is my complete code

        [HttpPost]
        public async Task<IActionResult> PostNewEmployee(IFormCollection formCollection)
        {
            if (!ModelState.IsValid) // check model validity
            {
                var errors = ModelState
                    .Select(x => x.Value)
                    .Where(y => y is not null && y.Errors.Count > 0)
                    .ToList();
    
    
                return BadRequest(JsonConvert.SerializeObject(errors));
            }
    
            // if the form has an Id then this is an existing person
            if (formCollection.TryGetValue("Id", out var existingId))
            {
                var realId = existingId.ToString(); 
    
                var existingUser = await _applicationDbContext.Users.FirstOrDefaultAsync(x => x.Id == realId); // get the current existing user
                if (existingUser is not null) // make sure the user exists
                {
    
                    await TryUpdateModelAsync(existingUser); // this will populate the current ``existingUser`` magically with the current model, it will only overwrite fields in the form, it will not touch properties that are NOT in the model
    
                    // update a foreign key
                    _applicationDbContext.Entry(existingUser.EmployeeAdditionalData).State = EntityState.Modified;
                    
                    await _applicationDbContext.SaveChangesAsync();
                    return Ok();
                }
            }
    
    

  2. You could cycle through the properties of the form with the reflection, then in the model, search for the properties with same name and type and then assign them.

    An example for this could be something like this:

    //Copy properties from FormPerson to EntityPerson
    var formPerson = new FormPerson();
    var entityPerson = new EntityPerson();
    formPerson.Name = "Marco";
    formPerson.Description = "Ciao come stai ?";
    
    foreach (var property in formPerson.GetType().GetProperties())
    {
        var entityProperty = entityPerson.GetType().GetProperty(property.Name);
        if (entityProperty != null)
        {
            var formValue = property.GetValue(formPerson);
            entityProperty.SetValue(entityPerson, formValue);
        }
    }
    
    public class FormPerson
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }
    
    public class EntityPerson
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }
    

    Otherwise you could use a library like automapper to do the job for you with an automatic map.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search