MVC3非序列索引和DefaultModelBinder

MVC 3.0中的默认模型联编程序是否能够处理非顺序索引(对于简单和复杂的模型types)是真的吗? 我遇到的post表明它应该,但是在我的testing中,它似乎没有。

给定回发值:

items[0].Id = 10 items[0].Name = "Some Item" items[1].Id = 3 items[1].Name = "Some Item" items[4].Id = 6 items[4].Name = "Some Item" 

和一个控制器方法:

 public ActionResult(IList<MyItem> items) { ... } 

唯一加载的值是项目0和1; 第4项简单地被忽略。

我已经看到了许多解决scheme来生成自定义索引( 模型绑定到列表 ),但他们都似乎瞄准以前的版本的MVC,而且大多数是一个“重手”IMO。

我错过了什么吗?

我有这个工作,你必须记住添加一个共同的索引隐藏input,如在您引用的文章中所解释的:

name = Items.Index的隐藏input是关键部分

 <input type="hidden" name="Items.Index" value="0" /> <input type="text" name="Items[0].Name" value="someValue1" /> <input type="hidden" name="Items.Index" value="1" /> <input type="text" name="Items[1].Name" value="someValue2" /> <input type="hidden" name="Items.Index" value="3" /> <input type="text" name="Items[3].Name" value="someValue3" /> <input type="hidden" name="Items.Index" value="4" /> <input type="text" name="Items[4].Name" value="someValue4" /> 

希望这可以帮助

这个从Steve Sanderson的方法派生的帮助方法非常简单,可以用来锚定集合中的任何项目,而且似乎可以与MVC模型绑定一起工作。

 public static IHtmlString AnchorIndex(this HtmlHelper html) { var htmlFieldPrefix = html.ViewData.TemplateInfo.HtmlFieldPrefix; var m = Regex.Match(htmlFieldPrefix, @"([\w]+)\[([\w]*)\]"); if (m.Success && m.Groups.Count == 3) return MvcHtmlString.Create( string.Format( "<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", m.Groups[1].Value, m.Groups[2].Value)); return null; } 

例如,只需在EditorTemplate中或其他任何可以生成input的地方调用它,如下所示,以生成索引锚定隐藏variables(如果适用)。

 @model SomeViewModel @Html.AnchorIndex() @Html.TextBoxFor(m => m.Name) ... etc. 

我认为它比Steve Sanderson的方法有一些优势。

  1. 它与EditorFor和其他内置的机制来处理枚举。 因此,如果Items是视图模型上的IEnumerable<T>属性,则以下方式按预期工作:

    <ul id="editorRows" class="list-unstyled"> @Html.EditorFor(m => m.Items) @* Each item will correctly anchor allowing for dynamic add/deletion via Javascript *@ </ul>

  2. 这是更简单,不需要更多的魔术string。

  3. 您可以为数据types提供一个EditorTemplate / DisplayTemplate,如果不在列表中的某个项目上使用,它将不需要任何操作。

唯一的缺点是,如果绑定的根模型是可枚举的(即Action方法本身的参数,而不是简单的参数对象图中更深层的属性),则绑定将在第一个非顺序索引处失败。 不幸的是,DefaultModelBinder的.Indexfunction仅适用于非根对象。 在这种情况下,您唯一的select仍然是使用上面的方法。

您引用的文章是一个旧的(MVC2),但据我所知,这仍然是使用默认的模型绑定器build模绑定集合的事实上的方式。

如果你想要非顺序索引,就像Bassam说的那样,你将需要指定一个索引器。 索引器不需要是数字的。

我们使用Steve Sanderson的BeginCollectionItem Html Helper来实现这个function。 它会自动生成一个Guid的索引器。 我认为这是一个更好的方法比使用数字索引当您的收集项目HTML是不连续的。

这个星期我一直在努力,Bassam的回答是让我走上正轨的关键。 我有一个可以有一个数量字段的库存物品的dynamic列表。 我需要知道他们select了多less项目,除了项目列表可以从1到n不等。

我的解决scheme最后很简单。 我用两个属性创build了一个名为ItemVM的ViewModel。 ItemID和数量。 在发布后,我接受这些列表。 通过索引,所有项目都可以通过。即使数量为零。 你必须validation和处理它的服务器端,但迭代处理这个dynamic列表是微不足道的。

在我看来,我正在使用这样的东西:

 @foreach (Item item in Items) { <input type="hidden" name="OrderItems.Index" value="@item.ItemID" /> <input type="hidden" name="OrderItems[@item.ItemID].ItemID" value="@item.ItemID" /> <input type="number" name="OrderItems[@item.ItemID].Quantity" /> } 

这给了我一个基于0的索引列表,但控制器中的迭代从新的强types模型中提取所有必要的数据。

 public ActionResult Marketing(List<ItemVM> OrderItems) ... foreach (ItemVM itemVM in OrderItems) { OrderItem item = new OrderItem(); item.ItemID = Convert.ToInt16(itemVM.ItemID); item.Quantity = Convert.ToInt16(itemVM.Quantity); if (item.Quantity > 0) { order.Items.Add(item); } } 

然后,您将收到数量大于0的项目集合以及项目ID。

这个技术是在Visual Studio 2015中使用EF 6的MVC 5中工作的。也许这会帮助像我一样search这个解决scheme的人。

或者使用这个javascript函数来修复索引:(显然replaceEntityName和FieldName)

 function fixIndexing() { var tableRows = $('#tblMyEntities tbody tr'); for (x = 0; x < tableRows.length; x++) { tableRows.eq(x).attr('data-index', x); tableRows.eq(x).children('td:nth-child(1)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName1"); tableRows.eq(x).children('td:nth-child(2)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName2"); tableRows.eq(x).children('td:nth-child(3)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName3"); } return true; //- Submit Form - } 

我结束了一个更通用的HTML助手:

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; namespace Wallboards.Web.Helpers { /// <summary> /// Hidden Index Html Helper /// </summary> public static class HiddenIndexHtmlHelper { /// <summary> /// Hiddens the index for. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <param name="htmlHelper">The HTML helper.</param> /// <param name="expression">The expression.</param> /// <param name="index">The Index</param> /// <returns>Returns Hidden Index For</returns> public static MvcHtmlString HiddenIndexFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int index) { var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); var propName = metadata.PropertyName; StringBuilder sb = new StringBuilder(); sb.AppendFormat("<input type=\"hidden\" name=\"{0}.Index\" autocomplete=\"off\" value=\"{1}\" />", propName, index); return MvcHtmlString.Create(sb.ToString()); } } } 

然后将其包含在Razor视图的list元素的每个迭代中:

 @Html.HiddenIndexFor(m => m.ExistingWallboardMessages, i)