I have a function with the following definitions, the first is the default and the second is the POST
:
public IActionResult TenantTBMapping(int tenantId)
[HttpPost]
public IActionResult TenantTBMapping(TenantTBMappingViewModel model)
The model is defined like this:
public class TenantTBMappingViewModel
{
public int TenantId { get; set; }
public List<MappingConfigDetail> Details { get; set; }
public List<string>? ToNames { get; set; }
public List<SelectListItem>? Inputs { get; set; }
public List<SelectListItem>? MappingTables { get; set; }
}
And I have this standard HiddenFor
on the page's form:
@using (Html.BeginForm("TenantTBMapping", "Settings", FormMethod.Post))
{
@Html.HiddenFor(model => model.TenantId);
// ...
}
As far as I can tell, this is all normal and looks like the same pages I've built hundreds of times. But for some reason, the TenantId
property is failing to bind, and always gives a 0. If I pull the value from the form directly using
var test = Request.Form["TenantId"];
it even returns the correct value, so it's something with the model binding.
The only thing I can guess is that the structure of the form is somehow messing up the TenantId
binding. As you can see, the model contains multiple list fields, though the only one returned from the form is Details. I have edit fields for each property of each entry in details (i.e. Model.Details[i].MappingConfigId
), and the details and their fields are binding correctly.
This is using ASP.NET Core 8 MVC.
EDIT: I tried deleting everything from the form except the hidden field and the value bound correctly, so it's definitely something with the form structure. It's a bit complex to post on SO but I'll try my best to put a sample.
I have a component that accepts a list of a class called EditConfiguration
, and this is what generates my editable fields. Again, the fields themselves are working fine, they're just interfering with the binding of TenantId
somehow. I can post more details about this component/class if that will help.
@Html.HiddenFor(model => model.TenantId);
<div id="edit-mappings" style="display:none;">
@for (int i = 0; i < Model.Details.Count; i++)
{
MappingConfigDetail detail = Model.Details[i];
string prefix = "Model.Details[" + i.ToString() + "].";
<div class="info-edit list-edit">
<div>
<h2>@Model.ToNames[i]</h2>
</div>
@await Component.InvokeAsync("Edit", new { config = new List<EditConfiguration>() {
new EditConfiguration() {/*params for my EditConfiguration class*/ },
Edit 2: Screenshot of payload
Okay, I figured it out. Because the details fields were using the Model.
prefix, I also needed to do the same for TenantId. I don't exactly understand, but I guess because TenantId was by itself and the other fields specifically used Model.
, it needed to be uniform. (Perhaps removing `Model. from the other fields would have the same effect?)