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

为何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();
        }
    }
}

验证修改

修改完成后:

  1. 运行程序,选择用户和系统后提交表单。
  2. 查看请求参数,会看到参数名称与ViewModel属性结构完全匹配。
  3. 控制器中的ViewModel将正确获取到两个值,后续逻辑正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:24:56