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

ASP.NET Core PartialView用formserialize无法绑定实体类问题

问题:ASP.NET Core 测验创建功能的模型绑定失败

我要实现创建测验、动态添加问题和答案的功能,使用asp-for完成绑定,序列化表单后提交到控制器,但模型无法正确绑定。

实体类

Answer类

public class Answer
{
    [Key]
    public Guid Id { get; } = Guid.NewGuid();
    public string Content { get; set; }
    public Guid QuestionId { get; set; }
}

Question类

public class Question
{
    [Key]
    public Guid Id { get; } = Guid.NewGuid();
    public string Content { get; set; }
    public Guid QuizId { get; set; }
    public ICollection<Answer> Answers { get; set; } = new List<Answer>() { 
        new Answer() { Content = "Answeeeer" }, 
        new Answer() { Content = "Answeeeer2" },
        new Answer() { Content = "Answeeeer3" }
    };
}

Quiz类

public class Quiz
{
    [Key]
    public Guid Id { get; } = Guid.NewGuid();
    public string Name { get; set; }
    public ICollection<Question> Questions { get; set; } = new List<Question>() { };
}

Quiz视图

@model QuizIt_Tests.Entities.Quiz

@{
ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Quiz</h4>
<hr />
<div class="row">
<div class="w-75">
<form id="myform" asp-action="Create" method="post" class="p-3">

            <div class="form-group">
                <label asp-for="Name" class="control-label">Quiz Name</label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
    
            <div id="questionRows" class="form-group w-100">
                @Html.EditorFor(model => model.Questions)
                <button type="button" id="AddQuestion" class="btn btn-primary mt-2">Add Question</button>
            </div>
    
            <div class="form-group mt-2">
                <input id="submitbutton" type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>

</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}

    <script>
    
        $("#submitbutton").click(function (e) {
            e.preventDefault();
    
            console.log("Serialized Data: ");
            console.log($('#myform').serialize())
    
            $.ajax({
                async: true,
                data: $('#myform').serialize(),
                method: 'post',
                url: "Quizes/AddQuestion",
                success: function () {
                }
            });
    
        });
    
        $("#AddQuestion").click(function () {
    
            console.log("Serialized Data: ");
            console.log($('#myform').serialize())
            console.log($('#questionRows').serialize())
    
            $.ajax({
                url: '@Url.Action("AddBlankQuestion", "Quizes")',
                cache: false,
                success: function (html) { $("#questionRows").append(html); },
                error: function (xhr, status, error) {
                    console.log(xhr.responseText);
                }
            });
            return false;
        });
    
        $("a.deleteRow").on("click", function () {
            $(this).parents("div.editorRow:first").remove();
            return false;
        });
    </script>

}

Question部分视图

@model QuizIt_Tests.Entities.Question

<hr style="height: 4px; color: black;" />
<div class="w-100 p-3 px-5 my-3">

    <div class="form-group">
        <label asp-for="Content" class="control-label">Question</label>
        <input asp-for="Content" class="form-control" value="@Model.Content" />
        <span asp-validation-for="Content" class="text-danger"></span>
    </div>
    <input type="hidden" asp-for="Id" class="form-control" value="@Model.Id" />
    
    @{
        var answerrowid = "answerRows" + @Model.Id;
        var addanswerbtnid = "addAnswer" + @Model.Id;
    }
    <div id="@answerrowid" class="w-75">
        @Html.EditorFor(model => model.Answers)
    </div>
    <button type="button" class="btn btn-primary" id="@addanswerbtnid">Add Answer</button>

</div>

<script>

    console.log("#@addanswerbtnid");
    $("#@addanswerbtnid").click(function () {
        console.log("Add Answer clicked");
        $.ajax({
            url: '@Url.Action("AddBlankAnswer", "Quizes")',
            cache: false,
            success: function (html) {
                $("#@answerrowid").append(html);
            },
            error: function (xhr, status, error) {
                console.log(xhr.responseText);
            }
        });
        return false;
    });

</script> 

Answer部分视图

@model QuizIt_Tests.Entities.Answer

<div class="row" style="margin: 20px;">

    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    
    <div class="form-group">
        <label asp-for="Content" class="control-label">Answer Content</label>
        <input asp-for="Content" class="form-control" value="@Model.Content" />
        <span asp-validation-for="Content" class="text-danger"></span>
    </div>
    
    <input type="hidden" asp-for="Id" class="form-control" value="@Model.Id" />

</div>

控制器代码

public class QuizesController : Controller
{
    private readonly ApplicationDBContext _context;

    public QuizesController(ApplicationDBContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> Index()
    {
        return View(await _context.Quizzes.ToListAsync());
    }

    public IActionResult Create()
    {
        var newquiz = new Quiz();
        return View(newquiz);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(Quiz quiz)
    {
        if (ModelState.IsValid)
        {
            _context.Add(quiz);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(quiz);
    }

    public IActionResult AddBlankQuestion(Quiz model)
    {
        Question question = new Question();
        //question.Answers.Add(new Answer() { QuestionId = question.Id, Content = "Salaaam Necesem" });
        return PartialView("EditorTemplates/Question", question);
    }
    public IActionResult AddBlankAnswer(Question model)
    {
        var newanswer = new Answer() { QuestionId = model.Id, Content = String.Empty };
        return PartialView("EditorTemplates/Answer", newanswer);
    } 
}

问题排查与解决方法

1. 嵌套集合的表单字段命名不符合模型绑定要求

ASP.NET Core的模型绑定需要集合类型的字段名包含连续索引(如Questions[0].Content、Questions[0].Answers[0].Content),但当前动态添加的Question和Answer部分视图生成的字段没有带索引,导致绑定失败。

解决方法:

  • 使用EditorTemplate配合索引器,或者在动态添加时手动维护索引。
  • 提交表单时,确保所有集合项的字段名遵循ParentCollection[index].Property的格式。

2. 实体类的Id是只读属性

实体类中Id属性只有getter:public Guid Id { get; } = Guid.NewGuid();,模型绑定无法为只读属性赋值,会导致绑定失败或数据丢失。

解决方法:

  • 改为可读写属性:public Guid Id { get; set; } = Guid.NewGuid();,这样模型绑定可以正常处理,同时不影响默认值生成。

3. 动态添加的部分视图未传递正确的上下文

调用AddBlankQuestion和AddBlankAnswer时,没有传递当前Quiz/Question的绑定上下文,导致生成的asp-for字段名没有包含父级集合的索引。

解决方法:

  • 在添加空白Question时,需要传递当前Quiz的Questions集合的当前索引,或者在返回PartialView时指定模板的前缀:
public IActionResult AddBlankQuestion()
{
    var question = new Question();
    var index = Request.Query["index"].ToString();
    ViewData.TemplateInfo.HtmlFieldPrefix = $"Questions[{index}]";
    return PartialView("EditorTemplates/Question", question);
}
  • 前端点击AddQuestion时,先获取当前Question的数量作为参数传递:
$("#AddQuestion").click(function () {
    var questionCount = $("#questionRows .editorRow").length;
    $.ajax({
        url: '@Url.Action("AddBlankQuestion", "Quizes")?index=' + questionCount,
        cache: false,
        success: function (html) { $("#questionRows").append(html); },
        error: function (xhr, status, error) {
            console.log(xhr.responseText);
        }
    });
    return false;
});

4. 提交AJAX请求的URL错误

当前提交按钮的AJAX请求URL是"Quizes/AddQuestion",但该方法仅用于获取空白视图,无法处理表单提交,应提交到Create方法。

解决方法:

  • 修改AJAX的URL为'@Url.Action("Create", "Quizes")',确保提交到正确的处理方法。

5. 缺少关联ID的绑定

动态添加的Question没有绑定QuizId,Answer没有绑定QuestionId,导致提交后关联字段为空。

解决方法:

  • 在Question部分视图中添加隐藏字段:<input type="hidden" asp-for="QuizId" />,并在创建Question时设置对应QuizId。
  • 在Answer部分视图中添加隐藏字段:<input type="hidden" asp-for="QuestionId" />,确保提交时关联ID正确。

内容的提问来源于stack exchange,提问作者Mətin Abaszadə

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 11:05:23