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

ASP.NET Core 3.0 MVC嵌套对象CRUD:动态添加文档表单问题求助

解决方案:ASP.NET Core动态添加表单行并正确绑定列表模型

你的问题核心在于动态生成的表单元素没有符合ASP.NET Core模型绑定要求的name属性格式,导致提交后Documents列表无法被正确绑定。下面是针对ASP.NET Core的完整解决方案,不需要依赖第三方包,完全用原生方式实现:

1. 调整主视图(AddFolder.cshtml)

我们需要修改视图,确保动态添加的文档行拥有正确的name属性格式(Documents[唯一索引].Name、Documents[唯一索引].Path)。ASP.NET Core的模型绑定支持非连续的索引(甚至字符串索引),所以可以用GUID生成唯一标识:

@model Folder
<div>
    <h3>Add Folder</h3>
    <input id="add-document" type="button" value="Add Document" class="btn btn-primary" />
</div>
<form asp-controller="Folder" asp-action="AddFolder" method="post" role="form">
    <div class="form-group form-row">
        <label asp-for="Name" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <input asp-for="Name" type="text" class="form-control" />
            <span asp-validation-for="Name" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group form-row">
        <table class="table table-hover">
            <thead>
                <tr>
                    <th>DOCUMENT NAME</th>
                    <th>DOCUMENT PATH</th>
                    <th></th>
                </tr>
            </thead>
            <tbody id="documentRows">
                <!-- 初始添加一行空文档行 -->
                @await Html.PartialAsync("_DocumentRow", new Document(), new ViewDataDictionary(ViewData) { { "Index", Guid.NewGuid().ToString() } })
            </tbody>
        </table>
    </div>
    <div class="form-group form-row">
        <div class="col-sm-10 offset-sm-2">
            <button type="submit" class="btn btn-success">Save Folder</button>
        </div>
    </div>
</form>

<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        $("#add-document").click(function () {
            // 生成唯一索引
            const index = generateGuid();
            // 请求新的文档行局部视图
            $.ajax({
                url: '@Url.Action("GetNewDocumentRow")',
                data: { index: index },
                type: "GET",
                success: function (html) {
                    $("#documentRows").append(html);
                }
            });
        });

        // 新增删除行功能
        $(document).on("click", ".delete-row", function () {
            $(this).closest("tr").remove();
        });
    });

    // 辅助生成GUID的函数
    function generateGuid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            const r = Math.random() * 16 | 0;
            const v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
</script>

2. 创建文档行局部视图(_DocumentRow.cshtml)

这个局部视图负责生成单个文档的表单行,使用传入的索引构造符合模型绑定要求的name属性:

@model Document
@{
    var index = ViewData["Index"] as string;
}
<tr>
    <td>
        <input type="text" 
               name="Documents[@index].Name" 
               value="@Model.Name" 
               class="form-control" 
               placeholder="Enter document name" />
        <span class="text-danger" data-valmsg-for="Documents[@index].Name" data-valmsg-replace="true"></span>
    </td>
    <td>
        <input type="text" 
               name="Documents[@index].Path" 
               value="@Model.Path" 
               class="form-control" 
               placeholder="Enter document path" />
        <span class="text-danger" data-valmsg-for="Documents[@index].Path" data-valmsg-replace="true"></span>
    </td>
    <td>
        <button type="button" class="btn btn-sm btn-danger delete-row">Delete</button>
    </td>
</tr>

3. 调整控制器方法

添加返回文档行局部视图的方法,以及处理表单提交的方法:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;

public class FolderController : Controller
{
    // 返回新的文档行局部视图
    public IActionResult GetNewDocumentRow(string index)
    {
        return PartialView("_DocumentRow", new Document(), new ViewDataDictionary(ViewData) { { "Index", index } });
    }

    // 处理表单提交
    [HttpPost]
    public IActionResult AddFolder(Folder folder)
    {
        if (ModelState.IsValid)
        {
            // 执行你的保存逻辑,比如存入数据库
            // _dbContext.Folders.Add(folder);
            // await _dbContext.SaveChangesAsync();
            return RedirectToAction("Index");
        }
        // 验证失败时返回原视图
        return View(folder);
    }
}

// 你的原有模型类
public class Folder
{
    public Folder() { Documents = new List<Document>(); }
    public string Name { get; set; }
    public List<Document> Documents { get; set; }
}

public class Document
{
    public string Name { get; set; }
    public string Path { get; set; }
}

关键说明

  • 模型绑定格式:ASP.NET Core要求列表项的表单元素name属性必须为[列表属性名][索引].[属性名](比如Documents[abc123].Name),这样模型绑定器才能识别并将其添加到Folder.Documents列表中。
  • 唯一索引:使用GUID作为索引可以避免重复,而且模型绑定不要求索引是连续数字,只要唯一即可。
  • 验证支持:如果需要客户端验证,确保页面引入了jquery.validate.js和jquery.validate.unobtrusive.js脚本,视图中的验证消息元素会自动显示错误提示。

为什么你的第二种方案失败?

你使用的Html.BeginCollectionItem是ASP.NET MVC时代的第三方扩展,在ASP.NET Core中没有内置支持,且它生成的name属性格式不符合ASP.NET Core的模型绑定规则,导致提交后列表为空。上面的方案完全基于ASP.NET Core原生功能,不需要额外依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 09:02:46