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ə
相关产品推荐
相关产品推荐

