Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor Pages - Model validation fails due to multiple objects sharing parameters

I am running into an issue that I cannot seem to wrap my head around. I am using Razor Pages and have two objects that can be bound.

[BindProperty]
public MeetingMinuteInputDto MeetingToCreate { get; set; }

[BindProperty]
public MeetingMinuteUpdateDto MeetingToUpdate { get; set; }

Above two are separate dto for creating/updating a base entity in my database. I have two separate dto because I allow only specific items for update (to prevent overposting). Both classes have a Name, Date, and Reminder. The MeetingMinuteUpdateDto only allows the Date to be changed.

The Name property is required and cannot be null. The reason I have both objects in the same page/controller is because I am using modals to create/update and I would rather not create multiple pages just to create/edit objects.

I have two forms that users can fill out - one for editing and one for creating. Each one binds the values to a specific object (i.e. the create form will bind its posted form values to the MeetingMinuteInputDto).

<div class="form-group">
    <div class="col-md-10">
          <label asp-for="MeetingToCreate.Name" class="control-label"></label>
          <input asp-for="MeetingToCreate.Name" class="form-control" />
          <span asp-validation-for="MeetingToCreate.Name" class="text-danger"></span>
      </div>
</div>

Above is a sample of my form for the creating a new meeting. I checked to see that when the form is submitted, only the MeetingMinuteInputDto value is being bound to. The other object (MeetingMinuteUpdateDto) has null values for all its properties.

But when I check the model state, MVC throws an error on the "Name" property saying that it is null. I looked at the results of the model state and there is a key named "Name" which is not tied to any object that fails validation.

If I remove the other object (i.e. I remove MeetingMinuteUpdateDto) from the page and do model binding, everything works correctly. How do I prevent the model validation from trying to validate an objects that is not relevant for the current action? I want the Create action to only validate the create object and vice versa.

I tried doing TryValidateModel(MeetingToCreate) but that also provides a false for model validation.

enter image description here

Note: I can't just place the object properties outside as I have other pages where I need to do this in which the update/create objects have 10+ properties that are shared/not shared.


Update - I can manually remove the validation error from the model state dictionary. But I don't really like that approach as I don't want to have to iterate through all the properties for the incorrect keys and remove them.


Update 2: Here is more detail on when this issue is occurring:

public class MeetingMinuteInputDto: MeetingMinuteManipulationDto
    {
        public string AdditionalInfo { get; set; }
    }

public class MeetingMinuteUpdateDto : MeetingMinuteManipulationDto
    {
    }

public class MeetingMinuteManipulationDto
    {
        [Required]
        public string Name { get; set; }
        public IFormFile FileToUpload { get; set; }
    }

In my Razor Page, I have both of these properties with the bind property attribute:

public class MeetingMinutesModel : PageModel
    {
        [BindProperty]
        public MeetingMinuteInputDto MeetingToCreate { get; set; }

        [BindProperty]
        public MeetingMinuteUpdateDto MeetingToUpdate { get; set; } 
        //...stuff
}

My form on the create/edit page looks as such:

<form asp-page="/MeetingMinute/MeetingMinutes" asp-page-handler="CreateMeeting">
    <div class="form-group">
        <div class="col-md-10">
              <label asp-for="MeetingToCreate.Name" class="control-label"></label>
              <input asp-for="MeetingToCreate.Name" class="form-control" />
              <span asp-validation-for="MeetingToCreate.Name" class="text-danger"></span>
          </div>
    </div>
    //other inputs and label for rest of the properties
   <button type="submit" class="btn btn-danger">Submit</button>
</form>

The input for the form is set to bind specifically to the MeetingToCreate property. When I submit the form, the form values are correctly bound. But at the same time, I am getting ModelState errors for the MeetingToUpdate property (as it shares some fields with MeetingToCreate).

I am assuming that in a Razor Page, model state validation is validating the MeetingMinutesModel and not the individual MeetingToCreate/MeetingToUpdate properties. In MVC, I assume this might be different as actions need to have parameters as opposed to open-ended properties on the page model in razor pages.

like image 512
Help123 Avatar asked Jan 07 '19 03:01

Help123


2 Answers

I was doing something similar. Searching for any solution, I find your question, but not a solution I share the solution.

I implement multiple BindProperty and multiple Actions OnPost, and the solution I find is to do some in the Asp.Net MVC, using the [Bind] property.

In your case, it would be.

public class MeetingMinutesModel : PageModel
{
    //[BindProperty] remove it
    public MeetingMinuteInputDto MeetingToCreate { get; set; }

    //[BindProperty] remove it
    public MeetingMinuteUpdateDto MeetingToUpdate { get; set; }
    //...stuff
}

public IActionResult OnPost([Bind("Name, FileToUpload, AdditionalInfo")] MeetingMinuteInputDto MeetingToCreate)
{
    //Do somthing
}
like image 50
Khalifa Boulbayem Avatar answered Oct 14 '22 02:10

Khalifa Boulbayem


In Razor pages , @model in the page is PageModel and the "Name" property of MeetingMinuteManipulationDto class is Required , so model state validation validates all the properties in the PageModel . If you insist on the original idea , you could try to use ViewComponent to implement create/edit objects in the same razor view.

like image 36
Xueli Chen Avatar answered Oct 14 '22 03:10

Xueli Chen