Хорошо, я думаю, что вижу, что здесь происходит.
Во втором примере, где вы выполняли foreach, похоже, что ваш cshtml был примерно таким (символы @ могут быть неправильными):
foreach (var war in Model.WarrantyFeaturesVm) {
using (Html.BeginCollectionItem("WarrantyFeaturesVm")) {
Html.HiddenFor(m => war.FeatureId)
<span>@Html.DisplayFor(m => war.Name)</span>
Html.HiddenFor(m => war.HasFeature)
}
}
Поскольку BeginCollectionItem использует свой контекст для получения имен и идентификаторов HTML, именно поэтому у вас возникает «война» в идентификаторах и именах. Механизм связывания ищет свойство коллекции с именем «WarrantyFeaturesVm», которое он находит. Однако затем он ищет свойство с именем war в модели представления WarrantyFeaturesVm, которое не может найти и, следовательно, не связывает.
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191"
name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].war.FeatureId"
id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__war_FeatureId" .../>
В 3-м сценарии это похоже. Он ищет свойство коллекции WarranyFeaturesVm, которое он находит. Однако он ищет другой элемент коллекции.
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191"
name="WarrantyFeaturesVm[fe3fbc82-a2df-476d-a15a-dacd841df97e].WarrantyFeaturesVm[0].FeatureId"
id="WarrantyFeaturesVm_fe3fbc82-a2df-476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__FeatureId" .../>
Для правильного связывания ваш HTML должен выглядеть так же, как ваш первый пример HTML:
<input type="hidden" value="68ba9241-c409-4f4b-96da-cce13b127c1e"
name="WarrantyFeaturesVm.index" .../>
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191"
name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].FeatureId"
id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__FeatureId" .../>
Как я и намекнул в своем комментарии, вы можете добиться этого, поместив BeginCollectionItem и все, что он включает, в частичное представление. Частичное представление получит свой собственный контекст, поскольку ваши помощники будут использовать свойство представления @Model со строго типизированными помощниками, например так: @Html.WidgetFor(m => m.PropertyName).
С другой стороны, если вам действительно нужно, чтобы коллекция отображалась во внешнем представлении, я не вижу проблем с использованием обычного индексирования (на основе целых чисел) с циклом for и без BeginCollectionItem.
Обновление
Я выкопал этот старый пост от Фила Хаака . Выдержка:
... введя дополнительный скрытый ввод, вы можете разрешить произвольное
индексы. В приведенном ниже примере мы предоставляем скрытый ввод с
Индексный суффикс для каждого элемента, который нам нужно привязать к списку. Имя
каждый из этих скрытых входов одинаков, так как описано выше,
это даст модели связующему хорошую коллекцию индексов, чтобы посмотреть
за привязку к списку.
<form method="post" action="/Home/Create">
<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />
<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />
<input type="hidden" name="products.Index" value="caliente" />
<input type="text" name="products[caliente].Name" value="Salsa" />
<input type="text" name="products[caliente].Price" value="1.23" />
<input type="submit" />
</form>
BeginCollectionItem использует этот метод индексации, чтобы обеспечить привязку модели. Единственное отличие состоит в том, что он использует Guids вместо int как индексатор. Но вы можете вручную установить любой индексатор, как в примере с Филом выше.