为何ViewModel数据绑定失效?附ASP.NET Core相关代码
问题:ASP.NET Core 嵌套ViewModel数据绑定失败
我有一个包含UserSystemPermission和SystemModel两个模型的ViewModel,需要从视图传递UserSystemPermission.UserId和SystemModel.SystemId并绑定到ViewModel。通过浏览器网络工具能看到数据已传输,但ViewModel始终无法获取到数据——实例化ViewModel时,内部两个模型为空;不实例化则ViewModel内的模型是null。
以下是相关代码:
ViewModel代码
//UserSystemPermissionsViewModel using SPLWeb.Models.DB; namespace SPLWeb.ViewModels { public class UserSystemPermissionsViewModel { public UserSystemPermission? UserSystemPermission { get; set; } public SystemModel? SystemModel { get; set; } } }
模型代码
SystemModel
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace SPLWeb.Models.DB; public partial class SystemModel { [Key] public int SystemId { get; set; } public string? SystemName { get; set; } public string? Description { get; set; } public bool DisabledBit { get; set; } public string? ChangedBy { get; set; } public DateTime? ChangedDate { get; set; } public virtual ICollection<FacilitySystem> FacilitySystems { get; set; } = new List<FacilitySystem>(); public virtual ICollection<SystemJob> SystemJobs { get; set; } = new List<SystemJob>(); public virtual ICollection<SystemUser>? SystemUsers { get; set; } public virtual ICollection<SystemPermission> SystemPermissions { get; set; } = new List<SystemPermission>(); }
UserSystemPermission
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace SPLWeb.Models.DB; public partial class UserSystemPermission { [Key] public int UserSystemPermissionId { get; set; } public int? UserId { get; set; } public int? SystemPermissionId { get; set; } public string? Notes { get; set; } public bool Active { get; set; } public string? AddedByUser { get; set; } public string? DeactivatedBy { get; set; } public string? ReactivatedBy { get; set; } public DateTime? LastModifiedDate { get; set; } public virtual SystemPermission? SystemPermission { get; set; } public virtual User? User { get; set; } }
视图代码
@model SPLWeb.ViewModels.UserSystemPermissionsViewModel @{ ViewData["Title"] = "Create"; } <h1>Create</h1> <h4>UserSystemPermission</h4> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="PresentSystemPermissionSelect" > <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label for="UserSystemPermission.UserId" class="control-label"></label> <select for="UserSystemPermission.UserId" class ="form-control" required="required" asp-items="ViewBag.UserList"></select> </div> <div class="form-group"> <label for="SystemModel.SystemId" class="control-label"></label> <select for="SystemModel.SystemId" class ="form-control" required="required" asp-items="ViewBag.SystemList"></select> </div> <div class="form-group"> <input 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");} }
控制器方法
public async Task<IActionResult> PresentSystemPermissionSelect([Bind("UserSystemPermission.UserId", "SystemModel.SystemId")] UserSystemPermissionsViewModel userSystemPermissionsViewModel) { var systemPermisionList = (from s in _context.SystemPermissions orderby s.Description where s.SystemId == userSystemPermissionsViewModel.SystemModel.SystemId select s).ToList(); ViewData["UserList"] = new SelectList(_context.Users.OrderBy(s => s.NameLast).OrderBy(s => s.NameFirst), "UserId", "FullName", userSystemPermissionsViewModel.UserSystemPermission.UserId); ViewData["SystemList"] = new SelectList(_context.SystemModels.OrderBy(e => e.SystemName), "SystemId", "SystemName", userSystemPermissionsViewModel.SystemModel.SystemId); ViewData["SystemPermissionList"] = new SelectList(_context.SystemPermissions.OrderBy(e => e.Description), "SystemPermissionId", "Description"); return View("SystemPermissionSelect", systemPermisionList); }
解决方案
问题出在视图标签助手的使用和ModelBinder的绑定规则上:
1. 视图中替换for为asp-for标签助手
视图里的<select>用了普通的for属性,没有启用ASP.NET Core的模型绑定标签助手,导致表单提交的参数名称无法正确映射到嵌套ViewModel的属性。
修改后的视图代码:
@model SPLWeb.ViewModels.UserSystemPermissionsViewModel @{ ViewData["Title"] = "Create"; } <h1>Create</h1> <h4>UserSystemPermission</h4> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="PresentSystemPermissionSelect" method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="UserSystemPermission.UserId" class="control-label"></label> <select asp-for="UserSystemPermission.UserId" class="form-control" asp-items="ViewBag.UserList"></select> <span asp-validation-for="UserSystemPermission.UserId" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="SystemModel.SystemId" class="control-label"></label> <select asp-for="SystemModel.SystemId" class="form-control" asp-items="ViewBag.SystemList"></select> <span asp-validation-for="SystemModel.SystemId" class="text-danger"></span> </div> <div class="form-group"> <input 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");} }
- 替换
for为asp-for,标签助手会自动生成匹配ViewModel嵌套结构的name属性(如UserSystemPermission.UserId)。 - 指定表单
method="post",更适合数据提交场景。 - 添加
asp-validation-for显示验证错误,方便调试。
2. 控制器优化绑定与空值判断
原控制器直接访问嵌套属性会导致空引用异常,需添加验证和空值检查:
修改后的控制器方法:
[HttpPost] public async Task<IActionResult> PresentSystemPermissionSelect([FromForm] UserSystemPermissionsViewModel userSystemPermissionsViewModel) { if (!ModelState.IsValid) { ViewData["UserList"] = new SelectList(_context.Users.OrderBy(s => s.NameLast).ThenBy(s => s.NameFirst), "UserId", "FullName"); ViewData["SystemList"] = new SelectList(_context.SystemModels.OrderBy(e => e.SystemName), "SystemId", "SystemName"); return View("Create", userSystemPermissionsViewModel); } if (userSystemPermissionsViewModel.SystemModel?.SystemId == null || userSystemPermissionsViewModel.UserSystemPermission?.UserId == null) { ModelState.AddModelError(string.Empty, "请选择完整的用户和系统信息"); ViewData["UserList"] = new SelectList(_context.Users.OrderBy(s => s.NameLast).ThenBy(s => s.NameFirst), "UserId", "FullName", userSystemPermissionsViewModel.UserSystemPermission?.UserId); ViewData["SystemList"] = new SelectList(_context.SystemModels.OrderBy(e => e.SystemName), "SystemId", "SystemName", userSystemPermissionsViewModel.SystemModel?.SystemId); return View("Create", userSystemPermissionsViewModel); } var systemPermisionList = await _context.SystemPermissions .Where(s => s.SystemId == userSystemPermissionsViewModel.SystemModel.SystemId) .OrderBy(s => s.Description) .ToListAsync(); ViewData["UserList"] = new SelectList(_context.Users.OrderBy(s => s.NameLast).ThenBy(s => s.NameFirst), "UserId", "FullName", userSystemPermissionsViewModel.UserSystemPermission.UserId); ViewData["SystemList"] = new SelectList(_context.SystemModels.OrderBy(e => e.SystemName), "SystemId", "SystemName", userSystemPermissionsViewModel.SystemModel.SystemId); ViewData["SystemPermissionList"] = new SelectList(_context.SystemPermissions.OrderBy(e => e.Description), "SystemPermissionId", "Description"); return View("SystemPermissionSelect", systemPermisionList); }
- 添加
[HttpPost]匹配表单提交方法。 - 使用
[FromForm]明确数据来源,提升可读性。 - 增加
ModelState.IsValid检查绑定是否成功。 - 添加空值判断,避免空引用异常并返回错误提示。
- 将同步查询改为异步
ToListAsync(),符合ASP.NET Core最佳实践。
3. 可选:ViewModel初始化优化
在ViewModel构造函数中初始化嵌套模型,避免绑定时空引用:
namespace SPLWeb.ViewModels { public class UserSystemPermissionsViewModel { public UserSystemPermission? UserSystemPermission { get; set; } public SystemModel? SystemModel { get; set; } public UserSystemPermissionsViewModel() { UserSystemPermission = new UserSystemPermission(); SystemModel = new SystemModel(); } } }
验证修改
修改完成后:
- 运行程序,选择用户和系统后提交表单。
- 查看请求参数,会看到参数名称与ViewModel属性结构完全匹配。
- 控制器中的ViewModel将正确获取到两个值,后续逻辑正常执行。
内容的提问来源于stack exchange,提问作者Doug
相关产品推荐
相关产品推荐

