C# MVC 表单无法发布对象列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19964553/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 16:15:03  来源:igfitidea点击:

MVC Form not able to post List of objects

c#asp.net-mvcasp.net-mvc-4asp.net-mvc-partialview

提问by Sonoilmedico

so I have an MVC Asp.net app that is having issues. Essentially, I have a View that contains a form, and its contents are bound to a list of objects. Within this loop, it loads PartialView's with the items being looped over. Now everything works up till the submittion of the form. When it gets submitted, the controller is sent a null list of objects. The code below demonstates the problems.

所以我有一个有问题的 MVC Asp.net 应用程序。本质上,我有一个包含表单的视图,其内容绑定到一个对象列表。在这个循环中,它加载 PartialView 和被循环的项目。现在一切正常,直到提交表单。当它被提交时,控制器被发送一个空的对象列表。下面的代码演示了这些问题。

Parent View:

父视图:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @foreach (var planVM in Model)
    {
        @Html.Partial("_partialView", planVM)
    }
</div>
}

_partialView:

_部分视图:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

And these are the classes for the above code:

这些是上述代码的类:

PlanViewModel:

平面视图模型:

public class PlansCompareViewModel
{

    public int PlanID { get; set; }
    public Plan CurrentPlan { get; set; }
    public bool ShouldCompare { get; set; }
    public PlansCompareViewModel(Plan plan)
    {
        ShouldCompare = false;
        PlanID = plan.PlanId;
        CurrentPlan = plan;
    }

    public PlansCompareViewModel()
    {
        // TODO: Complete member initialization
    }
    public static IEnumerable<PlansCompareViewModel> CreatePlansVM(IEnumerable<Plan> plans)
    {
        return plans.Select(p => new PlansCompareViewModel(p)).AsEnumerable();
    }
}

Controller:

控制器:

public class PlansController : MyBaseController
{
    [HttpPost]
    public ActionResult ComparePlans(IEnumerable<PlanCompareViewModel> model)
    {
         //the model passed into here is NULL
    }
}

And the problem is in the controller action. As far as I am aware, it should be posting an enumerable list of PlanCompareViewModels, yet it is null. When in inspect the post data being sent, it is sending the correct params. And if I were to change 'IEnumerable' to 'FormCollection', it contains the correct values. Can anyone see why the binder is not creating the correct object? I can get around this using javascript, but that defeats the purpose! Any help would be greatly appreciated!

问题出在控制器动作上。据我所知,它应该发布一个可枚举的 PlanCompareViewModel 列表,但它为空。在检查正在发送的发布数据时,它正在发送正确的参数。如果我将“IEnumerable”更改为“FormCollection”,它会包含正确的值。谁能看出为什么活页夹没有创建正确的对象?我可以使用 javascript 来解决这个问题,但这违背了目的!任何帮助将不胜感激!

采纳答案by John H

Your model is nullbecause the way you're supplying the inputs to your form means the model binder has no way to distinguish between the elements. Right now, this code:

您的模型是null因为您向表单提供输入的方式意味着模型绑定器无法区分元素。现在,这段代码:

@foreach (var planVM in Model)
{
    @Html.Partial("_partialView", planVM)
}

is not supplying any kind of index to those items. So it would repeatedly generate HTML output like this:

没有为这些项目提供任何类型的索引。所以它会重复生成这样的 HTML 输出:

<input type="hidden" name="yourmodelprefix.PlanID" />
<input type="hidden" name="yourmodelprefix.CurrentPlan" />
<input type="checkbox" name="yourmodelprefix.ShouldCompare" />

However, as you're wanting to bind to a collection, you need your form elements to be named with an index, such as:

但是,当您想要绑定到一个集合时,您需要使用索引命名表单元素,例如:

<input type="hidden" name="yourmodelprefix[0].PlanID" />
<input type="hidden" name="yourmodelprefix[0].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[0].ShouldCompare" />
<input type="hidden" name="yourmodelprefix[1].PlanID" />
<input type="hidden" name="yourmodelprefix[1].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[1].ShouldCompare" />

That index is what enables the model binder to associate the separate pieces of data, allowing it to construct the correct model. So here's what I'd suggest you do to fix it. Rather than looping over your collection, using a partial view, leverage the power of templates instead. Here's the steps you'd need to follow:

该索引使模型绑定器能够关联单独的数据片段,从而构建正确的模型。所以这就是我建议你修复它的方法。与其使用局部视图循环遍历您的集合,不如利用模板的强大功能。以下是您需要遵循的步骤:

  1. Create an EditorTemplatesfolder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\EditorTemplates).
  2. Create a strongly-typed view in that directory with the name that matches your model. In your case that would be PlanCompareViewModel.cshtml.
  1. EditorTemplates在您的视图的当前文件夹中创建一个文件夹(例如,如果您的视图是Home\Index.cshtml,则创建文件夹Home\EditorTemplates)。
  2. 在该目录中使用与您的模型匹配的名称创建一个强类型视图。在你的情况下,那将是PlanCompareViewModel.cshtml.

Now, everything you have in your partial view wants to go in that template:

现在,您在局部视图中拥有的所有内容都想放入该模板中:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

Finally, your parent view is simplified to this:

最后,您的父视图简化为:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @Html.EditorForModel()
</div>
}

DisplayTemplatesand EditorTemplatesare smart enough to know when they are handling collections. That means they will automatically generate the correct names, including indices, for your form elements so that you can correctly model bind to a collection.

DisplayTemplates并且EditorTemplates足够聪明,知道他们何时处理集合。这意味着它们将自动为您的表单元素生成正确的名称,包括索引,以便您可以正确地对集合进行建模。

回答by LINQ2Vodka

Please read this: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
You should set indicies for your html elements "name" attributes like planCompareViewModel[0].PlanId, planCompareViewModel[1].PlanIdto make binder able to parse them into IEnumerable.
Instead of @foreach (var planVM in Model)use forloop and render names with indexes.

请阅读:http: //haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
您应该为您的 html 元素“名称”属性设置索引,例如planCompareViewModel[0].PlanIdplanCompareViewModel[1].PlanId以使活页夹能够将它们解析为 IEnumerable。
而不是@foreach (var planVM in Model)使用for带有索引的循环和渲染名称。