使用带有Razor的IEnumerable <model>的MVC在发布到控制器后为null

时间:2017-11-30 00:54:44

标签: post model-view-controller model

我的模特

public class ActivityModel
{
    public int Id { get; set; }
    public String Nick { get; set; }
    public String TripName { get; set; }
    [DisplayFormat(DataFormatString = "{0:hh\\:mm}", ApplyFormatInEditMode = true)]
    public TimeSpan? FromTime { get; set; }
    [DisplayFormat(DataFormatString = "{0:hh\\:mm}", ApplyFormatInEditMode = true)]
    public TimeSpan? ToTime { get; set; }
    public String FromPlace { get; set; }
    public String ToPlace { get; set; }
    public String activityType { get; set; }
    [DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}", ApplyFormatInEditMode = true)]
    public DateTime? Timestamp { get; set; }
    public String Weather { get; set; }
    public int Difficulty { get; set; }
    public bool Reviewed { get; set; }
}

我的视图行如下所示

@model IList<ActivityModel>
    @for (int i = 0; i < Model.Count; i++)
{
    if (!(bool)Model[i].Reviewed)
    {
        <tr>
            @using (Html.BeginForm("Accept", "Overview", FormMethod.Post, new { activityModel = Model }))
            {
                @Html.AntiForgeryToken()
                <td>
                    @Html.DisplayFor(m => Model[i].Timestamp)
                    @Html.HiddenFor(m => Model[i].Timestamp)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].Nick)
                    @Html.HiddenFor(m => Model[i].Nick)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].TripName)
                    @Html.HiddenFor(m => Model[i].TripName)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].FromTime)
                    @Html.HiddenFor(m => Model[i].FromTime)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].FromPlace)
                    @Html.HiddenFor(m => Model[i].FromPlace)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].ToTime)
                    @Html.HiddenFor(m => Model[i].ToTime)
                </td>
                <td>
                    @Html.DisplayFor(m => Model[i].ToPlace)
                    @Html.HiddenFor(m => Model[i].ToPlace)
                </td>
                <td>
                    @Html.TextBoxFor(m => Model[i].Difficulty, new { id = "Difficulty" })
                    <--!@Html.HiddenFor(m => Model[i].Difficulty)--!>
                    <div style="visibility:hidden">
                        @Html.HiddenFor(m => Model[i].Id)
                        @Html.HiddenFor(m => Model[i].Reviewed)
                        @Html.HiddenFor(m => Model[i].activityType)
                        @Html.HiddenFor(m => Model[i].Weather)
                    </div>
                </td>
                <td>
                    <input type="submit" name="btn_accept" value="Accept" />
                </td>
            }
        </tr>
     }
}

我的控制器就是这个

public ActionResult Index()
{
    List<ActivityModel> activities_web = new List<ActivityModel>();    
    //someLogicForFilling
    return View(activities_web);
}

[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult Accept(ActivityModel activityModel)
{
    return RedirectToAction("Index");
}

问题是在ActionResult中是AcceptModel总是NULL空0 ... 我现在真的花了一整天时间,而且我不知道问题出在哪里,我尝试了近20个解决方案,对我来说没什么用。

渲染页面并显示所有值是正常的,但是当我尝试发布它们时,就像没有发布任何内容一样。

1 个答案:

答案 0 :(得分:0)

当发布的数据与您用作HttpPost操作方法参数的类的属性结构匹配时,模型绑定将起作用。

您当前的视图代码正在生成这样的HTML代码

<input name="[0].Id" type="hidden" value="101">
<input name="[1].Id" type="hidden" value="202">

假设您在集合中有2个项目,则传递给视图。

您可以看到name属性值为[0].Id[1].Id。但是,您的Accept操作方法的参数是单个ActivityModel对象,属性名称类似于IdFromPlace等。对于模型绑定工作,名称您的输入和属性名称应匹配。

您可以重写视图代码,为隐藏的输入生成正确的name属性值。您可以简单地使用纯HTML代码创建隐藏输入并设置模型项中的值或使用Html.HiddenFor辅助方法。

@foreach (var item in Model.Where(a => a.Reviewed == false))
{
    <input type="hidden" name="FromPlace" value="@item.FromPlace" />
    @Html.Hidden("ToPlace", item.ToPlace)
    @Html.Hidden(nameof(ActivityModel.Id), item.Id)
}

以下是完整代码(排除了一些节省空间的属性)。

@model IList<ActivityModel>
<table class="table table-striped">
    @foreach (var item in Model.Where(a => a.Reviewed == false))
    {
        using (Html.BeginForm("Accept", "Process", FormMethod.Post))
        {                   
            @Html.AntiForgeryToken()
            <tr>
                <td>
                    @Html.DisplayFor(m => item.FromPlace)
                    @Html.Hidden("FromPlace", item.FromPlace)
                </td>
                <td>
                    @Html.DisplayFor(m => item.ToPlace)
                    @Html.Hidden("ToPlace", item.ToPlace)
                </td>
                <td>
                    @Html.Hidden(nameof(ActivityModel.Id),item.Id)
                    <input type="submit" name="btn_accept" value="Accept" />
                </td>
            </tr>
        }
    }
</table>

但理想情况下,如果您尝试更新单个实体/记录的状态,则只需要该记录的Id(主键)。这意味着,您确实不需要填充所有属性。在这种情况下,您需要Id属性值的隐藏输入元素。如果需要,您始终可以使用此Id查询数据库以获取完整实体。 (绝不相信来自客户的数据)