You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Razor Pages foreach内textarea提交OnPost时IEnumerable对象为Null

问题根因

你的代码存在4个直接导致模型绑定失败的问题:

  • 集合属性类型不支持绑定:你定义的ExpendituresList是IEnumerable<Expenditure>类型,ASP.NET Core 集合模型绑定需要可按索引读写的集合类型(如List<T>),IEnumerable本身不支持索引写入操作,绑定器无法完成值填充。
  • 表单字段命名不符合绑定规则:ASP.NET Core 绑定集合时,要求表单字段的name属性必须遵循[集合属性名].[索引].[字段名]的格式。你使用foreach循环+FirstOrDefault()LINQ查询给asp-for赋值的写法,生成的textarea、隐藏域的name属性完全不满足该规则,绑定器无法识别字段和集合项的对应关系。
  • 漏写表单标签:你的表格和提交按钮没有包裹在method="post"的<form>标签内,默认情况下表单控件的值不会被提交到后端。
  • 隐藏域绑定错误:你写的@Html.HiddenFor(model => expenditure.ExpenditureItemId)生成的隐藏域name为expenditure.ExpenditureItemId,和目标集合属性ExpendituresList没有关联,提交后后端无法获取每个条目的主键值。
修复方案

1. 调整后端集合属性类型

将IEnumerable<Expenditure>改为List<Expenditure>,保证绑定器可以正常操作集合:

[BindProperty(SupportsGet = true)]
public List<Expenditure> ExpendituresList { get; set; } = new();

如果你的OnGet方法中是通过LINQ查询给该属性赋值,只需要在查询末尾加.ToList()即可,不需要修改原有查询逻辑。

2. 重写CSHTML页面代码

核心修改点:

  • 所有需要提交的内容包裹在post表单内
  • 用带索引的for循环替代foreach循环,直接通过索引访问集合项生成asp-for绑定,禁止在asp-for中使用LINQ查询方法
  • 按索引生成主键隐藏域,额外添加索引标记隐藏域兼容非连续索引场景
    修改后的完整代码:
@model WorkInProgress.Pages.Expenditures
<form method="post">
    <table class="table">
        <thead>
        <tr>
            <th class="text-start">Task Number</th>
            <th class="text-start">Expenditure Type</th>
            <th class="text-end">Item Date</th>
            <th class="text-end">Billable</th>
            <th class="text-end">Vendor Id</th>
            <th></th>
            <th class="text-start">Vendor Name</th>
            <th class="text-end">Revenue Amount</th>
            <th class="text-end">Comments</th>
        </tr>
        </thead>
        <tbody>
        @for (var i = 0; i < Model.ExpendituresList.Count; i++)
        {
            <tr>
                <td class="text-start">@Model.ExpendituresList[i].TaskNumber</td>
                <td class="text-start">@Model.ExpendituresList[i].ExpenditureType</td>
                <td class="text-end">@Model.ExpendituresList[i].ExpenditureItemDate.ToString("dd-MMM-yyyy")</td>
                <td class="text-end">@Model.ExpendituresList[i].BillableFlag</td>
                <td class="text-end">@Model.ExpendituresList[i].VendorId</td>
                <td></td>
                <td class="text-start">@Model.ExpendituresList[i].SupplierName</td>
                <td class="text-end">@Model.ExpendituresList[i].ProjFuncRevenueAmount</td>
                @* 集合索引标记,兼容非连续索引场景 *@
                <input type="hidden" asp-for="ExpendituresList[i].Index" value="@i" />
                @* 主键隐藏域,按索引绑定 *@
                <input type="hidden" asp-for="ExpendituresList[i].ExpenditureItemId" />
                <td class="text-end">
                    <textarea class="form-control" asp-for="ExpendituresList[i].ExpenditureComment" rows="1"></textarea>
                </td>
            </tr>
        }
        </tbody>
    </table>
    <div class="p-3">
        <button type="submit" class="btn btn-primary" asp-page-handler="Button">Save</button>
    </div>
</form>

3. 后端Post方法无需额外修改

绑定器会自动将提交的表单值填充到ExpendituresList属性中,你直接在OnPostButton方法中访问该属性即可获取所有行的备注和对应主键:

public IActionResult OnPostButton()
{
    // 此处ExpendituresList已完成绑定,可直接遍历处理保存逻辑
    foreach (var item in ExpendituresList)
    {
        var itemId = item.ExpenditureItemId;
        var comment = item.ExpenditureComment;
        // 你的业务保存逻辑
    }
    return RedirectToPage();
}
绑定原理说明

asp-for标签 helper在for循环中按索引生成的字段name属性格式为ExpendituresList[0].ExpenditureComment、ExpendituresList[0].ExpenditureItemId、ExpendituresList[1].ExpenditureComment,完全匹配ASP.NET Core集合模型绑定的命名约定,绑定器会自动按索引将表单值映射到集合对应项,不会出现空值问题。
注意:永远不要在循环中使用FirstOrDefault、Where等LINQ方法为asp-for赋值,这类写法生成的name属性无法被集合绑定器识别。


内容的提问来源于stack exchange,提问作者Baldie47

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:39:30