ASP.Net Model Binder - Ignore Not Mapped Properties

Viewed 557

I am working with ASP.Net Core 5, Entity Framework Core 6, and using ODATA controllers with an EDM model. The model I am working with has several properties, including a [NotMapped] property. I have a React SPA that gets an entity from the controller, including the [NotMapped] property. When I post this entity back to my controller from the react app. The controller uses [FromBody] attribute to deserialize the JSON into a complex .NET object. However, the model fails to bind because of the [NotMapped]` property. If I remove this not mapped property in the React app on the client side before posting to the controller, the model binder works fine. However, this is not an acceptable solution, since this means my code will break anytime I add a not mapped property to my models without updating the React client to deal with this. The React client has no understanding of which properties are mapped or not mapped. Is there a way to get the model binder to just ignore properties that do not exist in the model, instead of having the model fail to bind completely?

Here is my model:

public class Person
{
    public Person() { }

    [Key]
    public int Id { get; set; }

    public string FirstName { get; set; }
        
    public string LastName { get; set; }

    [NotMapped]
    public string FullName => FirstName + " " + LastName;
        
}

And here is my controller Get action:

    [ODataRoute("{id}")]
    public Person Get([FromODataUri] int id)
    { return db.Persons.Find(id); }

The controller successfully gets the Person, including the [NotMapped] FullName property from the model and returns the model to the React client.

When I update the model on the React client and want to update Person, I call the Put method of this controller:

    [ODataRoute("{id}")]
    public async Task<IActionResult> Put([FromODataUri] int id, [FromBody] Person person)
    {
        if (!ModelState.IsValid) return BadRequest(ModelState);
        if (id != person.Id) return BadRequest();

        db.Entry(person).State = EntityState.Modified;
        await db.SaveChangesAsync();
        return Updated(person);
    }

However, the Person object is null in the controller because it does not recognize the [NotMapped] FullName property of Person. I presume this, because if I remove this property from the JSON before POST'ing the data to the controller the model binder properly populate the Person object.

Is there a simple way to tell the model binder to simple ignore extra properties that do not exist as mapped properties of the model it is trying to bind, or do I have to write a complete custom model binder to do this?

Edit Here is an example JSON payload that results in the model not binding, and the Person entity in my controller is null:

{
  "@odata.context": "https://localhost:44304/rest/$metadata#Persons/$entity",
  "Id": 1,
  "FirstName": "Jimmy",
  "LastName": "Johnson",
  "FullName": "Jimmy Johnson",
}

Note the FullName property is [NotMapped]. When included in the JSON payload in my controller, it will not deserserialize. The person object is Null. Also notice there @odata.context does not interfere with the deserialization, even though it is not in the model. I assume the Odata inputformatter knows to expect that and ignores it.

If I send the same payload as above, but omit the FullName property, the JSON deserializes and the person object is populated.

I would like to figure out how to make it so that FullName or any [NotMapped] property is ignored, rather than causing the deserialization to fail. I am hoping for a solution that doesn't make me have a custom inputformatter for each entity type, and one that does not require me to make changes for every new [NotMapped] property I add to any of my models. It seems like there should just be an option in System.Text.Json options that tells the deserializer to ignore properties that don't exists for the model, just like @Odata.context which is ignored. However, I have not seen such an option.

0 Answers
Related