ASP.NET MVC如何传列表到视图填充下拉框 ViewBag使用疑问
问题
我有一个基于Category模型的Create视图,需要让用户通过下拉框选择Category存储所属的Warehouse(另一模型)。由于无法向单个视图传递2个视图模型,我该如何在Category.Create视图中获取Warehouse名称?
现有视图代码:
@model InventoryManSys.Models.Category @{ ViewData["Title"] = "Create"; } <h1>Create</h1> <h4>Category</h4> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="Create"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Name" class="control-label"></label> <input asp-for="Name" class="form-control" /> <span asp-validation-for="Name" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Warehouse" class="control-label"></label> <select asp-for="Warehouse" class ="form-control" asp-items="ViewBag.Warehouses"></select> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> </div> </div>
另外我听说使用ViewBag属于不良实践,请问该说法是否正确?
解答
下拉选项的实现方案
不要尝试给单个视图传递多个实体模型,正确做法是为这个页面单独建强类型视图模型(View Model),这是ASP.NET Core MVC下处理多数据源页面的标准方案:
- 先定义视图专用模型,只包含当前页面需要的字段,不要直接把数据库实体当视图模型用
注意你的Category实体需要配置using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc.Rendering; namespace InventoryManSys.Models.ViewModels; public class CategoryCreateVm { [Required(ErrorMessage = "请输入分类名称")] public string Name { get; set; } [Required(ErrorMessage = "请选择所属仓库")] public int WarehouseId { get; set; } public IEnumerable<SelectListItem> WarehouseOptions { get; set; } }WarehouseId外键属性,否则EF Core关联查询、表单模型绑定都会出问题。 - 在GET请求的Create Action中查询仓库数据,填充视图模型后传给视图
public IActionResult Create() { var createVm = new CategoryCreateVm { // 查询所有仓库,转成下拉框需要的选项格式 WarehouseOptions = _dbContext.Warehouses .OrderBy(w => w.Name) .Select(w => new SelectListItem { Text = w.Name, Value = w.Id.ToString() }) .ToList() }; return View(createVm); } - 修改视图,绑定新建的视图模型,下拉框直接取模型里的选项数据即可
@model InventoryManSys.Models.ViewModels.CategoryCreateVm @{ ViewData["Title"] = "新建分类"; } <h1>新建分类</h1> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="Create"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Name" class="control-label"></label> <input asp-for="Name" class="form-control" /> <span asp-validation-for="Name" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="WarehouseId" class="control-label">所属仓库</label> <select asp-for="WarehouseId" class="form-control" asp-items="Model.WarehouseOptions"> <option value="">-- 请选择仓库 --</option> </select> <span asp-validation-for="WarehouseId" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="保存" class="btn btn-primary" /> </div> </form> </div> </div> - POST提交的Action直接接收这个视图模型,校验通过后将字段映射回Category实体,存入数据库即可。
关于ViewBag的实践争议
ViewBag不是绝对不能用的“反模式”,但绝大多数业务场景下不推荐优先使用:
- ViewBag是动态类型,没有编译时检查,属性名拼写错误、类型不匹配的问题只能在运行时触发,排查成本高
- 开发时没有智能提示,写代码效率低
- 数据传递没有明确契约,Controller传了什么、视图需要什么数据,仅看Action方法签名完全无法判断,后续维护的人需要同时翻Controller和View代码才能理清楚数据流向
- 只有在传递零散的、全局通用的简单数据(比如页面标题、当前登录用户昵称这类)时,用ViewBag/ViewData才是性价比更高的选择,像下拉选项这类和页面业务强绑定的数据,用强类型视图模型的可维护性要高得多。
你原来代码里直接给asp-for="Warehouse"绑定整个Warehouse实体的写法是错的,表单提交只能传递简单类型值,绑定外键ID才是正确做法,否则模型绑定会直接失败。
内容的提问来源于stack exchange,提问作者Cohiba
相关产品推荐
相关产品推荐

