Collections are not correctly model-bound #2129
Comments
@branciat am I correct the partial view named _Observations.cshtml contains something like the following? @using System.Collections
@model List
...
@Html.EditorFor(m => m) If so you may get better results using |
@branciat Could you please share exactly what your cshtml view looks like and also the |
The partial view @model List<Mediagral.Core.Models.Observation>
<div class="portlet box green">
<div class="portlet-title">
<div class="caption">
<i class="fa fa-edit"></i>Observations
</div>
<div class="tools">
<a class="btn red" href="@Url.RouteUrl("CoreApi", new { area = "Core", controller = "Observation", action = "Create", id = Model.First().AddressableId })" data-ajax="true" data-ajax-mode="after"
data-ajax-update="#obs_table tbody" style="height:30px">
<i class="fa fa-plus"></i>
</a>
</div>
</div>
<div class="portlet-body form">
@if (ViewBag.ModeEdit)
{
Html.BeginForm(FormMethod.Post, new { data_ajax = "true", data_ajax_mode = "replace", data_ajax_update = "#AjaxContent" });
}
<div class="form-body">
<div class="table-scrollable no-footer">
<table aria-describedby="obs_info" role="grid" class="table table-condensed table-hover table-bordered no-footer" id="obs_table">
<thead>
<tr role="row">
<th>Contenu</th>
<th>Date</th>
<th>Créé par</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.Count; i++) {
<tr class="@((i % 2) == 0 ? "even" : "odd" )" role="row">
@if (ViewBag.ModeEdit)
{
<td>
@Html.AntiForgeryToken()
@Html.EditorFor(m => m[i].Content, new { HtmlAttributes = new { @class = "form-control" } })
</td>
<td>
@Html.HiddenFor(m => m[i].AddressableId)
@Html.EditorFor(m => m[i].Created, new { HtmlAttributes = new { @class = "form-control" } })
</td>
<td>
@Html.HiddenFor(m => m[i].Id)
@Html.EditorFor(m => m[i].CreatedBy.UserName, new { HtmlAttributes = new { @class = "form-control" } })
</td>
}
else
{
<td>@Html.DisplayFor(m => m[i].Content)</td>
<td>@Html.DisplayFor(m => m[i].Created)</td>
<td>@Html.DisplayFor(m => m[i].CreatedBy.UserName)</td>
}
<td>
<a class="btn default btn-sm purple" href="@Url.RouteUrl("CoreApi", new { area = "Core", controller = "Observation", action = "Delete", id = Model[i].Id })" data-ajax="true">
<i class="fa fa-trash-o"></i> Supprimer
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="form-actions">
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-offset-3 col-md-9">
@if (ViewBag.ModeEdit)
{
<button type="submit" class="btn green" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#AjaxContent">
<i class="fa fa-pencil"></i> Valider
</button>
<button type="reset" class="btn yellow" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#AjaxContent">
<i class="fa fa-pencil"></i> Annuler
</button>
}
else
{
<a href="@Url.RouteUrl("CoreApi", new { area = "Core", controller = "Observation", action = "Edit", id = Model.First().AddressableId })" class="btn green"
data-ajax="true" data-ajax-mode="replace" data-ajax-update="#AjaxContent">
<i class="fa fa-pencil"></i> Editer
</a>
}
</div>
</div>
</div>
</div>
</div>
@if(ViewBag.ModeEdit)
{
Html.EndForm();
}
</div>
</div> The entity using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Mediagral.User.Models;
namespace Mediagral.Core.Models
{
public partial class Observation
{
public int Id { get; set; }
public string CreatedById { get; set; }
public int AddressableId { get; set; }
[Required, DataType(DataType.Text)]
public string Content { get; set; }
[Column(TypeName = "datetime2")]
public DateTime Created { get; set; }
public virtual MediagralUser CreatedBy { get; set; }
public Addressable Addressable { get; set; }
}
} |
@branciat which part of the generated HTML is not what you expect? anything similarly unexpected in the HTML generated when |
@dougbu Presumably the name attribute is empty - we should try to reproduce this. Could you please try it out? Thanks! |
@dougbu |
@dougbu
|
@branciat did you edit the HTML slightly? everything looks as I'd expect except it should be a self-closing tag e.g. and what in particular is empty? |
Yes i forgot the tag |
the |
Yes it's the problem for each view with a collection as model.
I tried with Observation[], the parameter is always null. |
I add "id" in the name of the html generated with the web development toolbox of firefox, no problem, the FromForm hydrate the entity. |
Does the submission work correctly if you remove To explain: traditional model binding does a pass where it tries first using the parameter name as the binding prefix - /cc @harshgMSFT |
good timing @rynowak I just reproduced this issue locally. it occurs with or without I observe the data is submitted correctly and has the expected keys in the
|
Is this related to #1865? |
@harshgMSFT no, the collections are at different levels. also #1865 reproduces with MVC 5 and this bug does not. |
I see, the cause of concern seems to be similar for both bugs, i think this is because in https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/GenericModelBinder.cs#L29 we return a value even if there was no value bound. Which is why it does not fallback to empty prefix. We should fix this asap. |
might be why the bug's assigned to someone (me in this case). I'm debugging further... |
I'm interested by the workaround |
@branciat is If the former, subclass |
- #2129 - do not propagate results with `!IsModelSet`, allowing empty prefix fallback - adjust ComplexModelDtoModelBinder to at least fake-bind all properties - default values not consistently picked up otherwise
@branciat we're working on a fix for this. it should be in our dev feed later this week. for Beta3, the workaround is to add |
- #2129 - do not propagate results with `!IsModelSet`, allowing empty prefix fallback - adjust ComplexModelDtoModelBinder to at least fake-bind all properties - default values not consistently picked up otherwise
updating the subject because reported issue is about problems model binding collections. HTML generation is fine (at least in this respect 😈) |
- #2129 - do not propagate results with `!IsModelSet`, allowing empty prefix fallback - adjust ComplexModelDtoModelBinder to at least fake-bind all properties - default values not consistently picked up otherwise
- #2129 - do not propagate results with `!IsModelSet`, allowing empty prefix fallback - adjust ComplexModelDtoModelBinder to at least fake-bind all properties - default values not consistently picked up otherwise
- remaining `MutableObjectModelBinder` tests - functional test of #2129 scenario
- #2129 - do not propagate results with `!IsModelSet`, allowing empty prefix fallback - adjust `ComplexModelDtoModelBinder` to at least fake-bind all properties - default values not consistently picked up otherwise nit: correct 2 test names in `KeyValuePairModelBinderTest`
…e match - #1865 - change `MutableObjectModelBinder` to ignore exact match in value providers - had an incorrect assumption: don't want exact model name to match since this binder supports only complex objects - also ignored `BinderModelName`, value provider filtering, et cetera - reduces over-binding e.g. `[Required]` validation within missing properties also add more tests of #2129 scenarios
commit 94e326f |
Perfect ! Thanks |
I retrieve an list of items with the action below
The model's view is defined as
@model List<Mediagral.Core.Models.Observation>
there an example of input generated for a property with an attribut name that contain an indexed array without name.
The text was updated successfully, but these errors were encountered: