asp.net-mvc MVC 发布复杂对象列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26155607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MVC post a list of complex objects
提问by Carel
I have a FeedbackViewModel that contains a list of questions:
我有一个包含问题列表的 FeedbackViewModel:
public class FeedbackViewModel
{
public List<QuestionViewModel> Questions { get; set; }
}
This QuestionViewModel is an object that can be inherited by 5 different types of questions
这个 QuestionViewModel 是一个对象,可以继承 5 种不同类型的问题
public class QuestionViewModel
{
public string QuestionText { get; set; }
public string QuestionType { get; set; }
}
An example of one of the inheriting question types:
继承问题类型之一的示例:
public class SingleQuestionViewModel : QuestionViewModel
{
public string AnswerText { get; set; }
}
In the HttpGetof the Indexaction in the controller I get the questions from the database and add the correct question type in list of question in the FeedbackViewModelThen I render this model in the view:
在控制器HttpGet中的Index操作中,我从数据库中获取问题并在问题列表中添加正确的问题类型FeedbackViewModel然后我在视图中呈现此模型:
@using (Html.BeginForm())
{
//foreach (var item in Model.Questions)
for (int i = 0; i < Model.Questions.Count; i++)
{
<div class="form-group">
@Html.DisplayFor(modelItem => Model.Questions[i].QuestionText, new { @class = "control-label col-md-4" })
<div class="col-md-6">
@if (Model.Questions[i].QuestionType == "Single")
{
@Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "Multiple")
{
@Html.TextAreaFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "SingleSelection")
{
@Html.RadioButtonForSelectList(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectedAnswer,
(Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectionAnswers)
}
else if (Model.Questions[i].QuestionType == "MultipleSelection")
{
@Html.CustomCheckBoxList((Model.Questions[i] as OpenDataPortal.ViewModels.MultipleSelectionQuestionViewModel).AvailableAnswers)
}
else if (Model.Questions[i].QuestionType == "UrlReferrer")
{
@Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
</div>
</div>
<br />
}
<br />
<button type="submit">Submit</button>
}


Now, I simply can't get it to post the list of questions in the model. Is it even possible to post a list of different object types?
现在,我根本无法在模型中发布问题列表。甚至可以发布不同对象类型的列表吗?
Edit:Following is the list of data within the post that I discovered using Fiddler:
编辑:以下是我使用 Fiddler 发现的帖子中的数据列表:


回答by OzBob
After much research I've found two solutions:
经过大量研究,我找到了两种解决方案:
- One is to write HTML that has hardcoded Id's and Names
- Two is to convert your ICollection/IEnumerable to an Array or List (i.e IList something with an 'index'), and have an Array object in your BindingModel in your Controller POST Action.
- 一种是编写具有硬编码 ID 和名称的 HTML
- 二是将您的 ICollection/IEnumerable 转换为数组或列表(即 IList 带有“索引”的内容),并在您的控制器 POST 操作中的 BindingModel 中有一个 Array 对象。
Thanks to Phil Haack's (@haacked) 2008 blog post http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/Which is still relevant to how the default ModelBinder works today for MVC. (NB: the links in Phil's article to sample porject and extension methods are broken)
感谢 Phil Haack (@haacked) 2008 年的博客文章http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/这仍然与今天默认的 ModelBinder 的工作方式相关对于 MVC。(注意:Phil 文章中示例项目和扩展方法的链接已损坏)
HTML snippet that inspired me:
激发我灵感的 HTML 片段:
<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="submit" />
</form>
Post array looks a bit like:
Post 数组看起来有点像:
products.Index=cold&products[cold].Name=Beer&products[cold].Price=7.32&products.Index=123&products[123].Name=Chips&products[123].Price=2.23
Model:
模型:
public class CreditorViewModel
{
public CreditorViewModel()
{
this.Claims = new HashSet<CreditorClaimViewModel>();
}
[Key]
public int CreditorId { get; set; }
public string Comments { get; set; }
public ICollection<CreditorClaimViewModel> Claims { get; set; }
public CreditorClaimViewModel[] ClaimsArray {
get { return Claims.ToArray(); }
}
}
public class CreditorClaimViewModel
{
[Key]
public int CreditorClaimId { get; set; }
public string CreditorClaimType { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:N2}")]
public Decimal ClaimedTotalAmount { get; set; }
}
Controller GET:
控制器获取:
public async Task<ActionResult> Edit(int id)
{
var testmodel = new CreditorViewModel
{
CreditorId = 1,
Comments = "test",
Claims = new HashSet<CreditorClaimViewModel>{
new CreditorClaimViewModel{ CreditorClaimId=1, CreditorClaimType="1", ClaimedTotalAmount=0.00M},
new CreditorClaimViewModel{ CreditorClaimId=2, CreditorClaimType="2", ClaimedTotalAmount=0.00M},
}
};
return View(model);
}
Edit.cshtml:
编辑.cshtml:
@Html.DisplayNameFor(m => m.Comments)
@Html.EditorFor(m => m.Comments)
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().CreditorClaimType)
</th>
<th>
@Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().ClaimedTotalAmount)
</th>
</tr>
<!--Option One-->
@foreach (var item in Model.Claims)
{
var fieldPrefix = string.Format("{0}[{1}].", "Claims", item.CreditorClaimId);
<tr>
<td>
@Html.DisplayFor(m => item.CreditorClaimType)
</td>
<td>
@Html.TextBox(fieldPrefix + "ClaimedTotalAmount", item.ClaimedTotalAmount.ToString("F"),
new
{
@class = "text-box single-line",
data_val = "true",
data_val_number = "The field ClaimedTotalAmount must be a number.",
data_val_required = "The ClaimedTotalAmount field is required."
})
@Html.Hidden(name: "Claims.index", value: item.CreditorClaimId, htmlAttributes: null)
@Html.Hidden(name: fieldPrefix + "CreditorClaimId", value: item.CreditorClaimId, htmlAttributes: null)
</td>
</tr>
}
</table>
<!--Option Two-->
@for (var itemCnt = 0; itemCnt < Model.ClaimsArray.Count(); itemCnt++)
{
<tr>
<td></td>
<td>
@Html.TextBoxFor(m => Model.ClaimsArray[itemCnt].ClaimedTotalAmount)
@Html.HiddenFor(m => Model.ClaimsArray[itemCnt].CreditorClaimId)
</td></tr>
}
Form is processed in the Controller:
表单在控制器中处理:
Post Model:
岗位型号:
public class CreditorPostViewModel
{
public int CreditorId { get; set; }
public string Comments { get; set; }
public ICollection<CreditorClaimPostViewModel> Claims { get; set; }
public CreditorClaimPostViewModel[] ClaimsArray { get; set; }
}
public class CreditorClaimPostViewModel
{
public int CreditorClaimId { get; set; }
public Decimal ClaimedTotalAmount { get; set; }
}
Controller:
控制器:
[HttpPost]
public ActionResult Edit(int id, CreditorPostViewModel creditorVm)
{
//...
回答by Nick Hotalling
Thanks for pointing me in the right direction with this post. I was struggling to get the syntax right for binding a non-sequential IDictionary<string, bool>object. Not sure this is 100% correct, but this Razor code worked for me:
感谢您通过这篇文章为我指明正确的方向。我正在努力使绑定非顺序IDictionary<string, bool>对象的语法正确。不确定这是否 100% 正确,但是这个 Razor 代码对我有用:
<input type="hidden" name="MyDictionary.Index" value="ABC" />
<input type="hidden" name="MyDictionary[ABC].Key" value="ABC" />
@Html.CheckBox(name: "MyDictionary[ABC].Value", isChecked: Model.MyDictionary["ABC"], htmlAttributes: null)
If you need a checkbox, be sure to use Html.CheckBox instead of a standard HTML checkbox. The model will blow up if a value is not provided, and Html.CheckBox generates a hidden field to ensure a value is present when the checkbox is not checked.
如果您需要复选框,请务必使用 Html.CheckBox 而不是标准的 HTML 复选框。如果未提供值,模型将爆炸,并且 Html.CheckBox 会生成一个隐藏字段以确保未选中复选框时存在值。
回答by salli
Make sure you are rendering your view in order so that Model.Questions[i]renders in order.
确保按顺序渲染视图,以便按顺序Model.Questions[i]渲染。
For example, Model.Questions[0], Model.Questions[1], Model.Questions[2].
I noticed that if the order is not correct mvc model binder will only bind the first element.
例如,Model.Questions[0], Model.Questions[1], Model.Questions[2]。我注意到如果顺序不正确 mvc 模型绑定器只会绑定第一个元素。
回答by user942620
Using Razor you can implement the for loop using a dictionary as follows without making changes to your object:
使用 Razor,您可以使用字典实现 for 循环,如下所示,而无需更改您的对象:
@foreach (var x in Model.Questions.Select((value,i)=>new { i, value }))
{
if (Model.Questions[x.i].QuestionType == "Single")
{
@Html.EditorFor(modelItem => (modelItem.Questions[x.i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
...
}
The collection needs to be either a List or Array for this to work.
集合需要是列表或数组才能工作。
回答by Yitzhak Weinberg
I use this code maybe its can help
我使用此代码也许它可以帮助
<input type="hidden" name="OffersCampaignDale[@(item.ID)].ID" value="@(item.ID)" />
@Html.Raw(Html.EditorFor(modelItem => item.NameDale, new { htmlAttributes = new { @class = "form-control" } })
.ToString().Replace("item.NameDale", "OffersCampaignDale[" + item.ID+ "].NameDale").Replace("item_NameDale", "NameDale-" + item.ID))
@Html.ValidationMessageFor(modelItem => item.NameDale, "", new { @class = "text-danger" })

