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

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下处理多数据源页面的标准方案:

  1. 先定义视图专用模型,只包含当前页面需要的字段,不要直接把数据库实体当视图模型用
    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; }
    }
    
    注意你的Category实体需要配置WarehouseId外键属性,否则EF Core关联查询、表单模型绑定都会出问题。
  2. 在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);
    }
    
  3. 修改视图,绑定新建的视图模型,下拉框直接取模型里的选项数据即可
    @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>
    
  4. POST提交的Action直接接收这个视图模型,校验通过后将字段映射回Category实体,存入数据库即可。

关于ViewBag的实践争议

ViewBag不是绝对不能用的“反模式”,但绝大多数业务场景下不推荐优先使用:

  • ViewBag是动态类型,没有编译时检查,属性名拼写错误、类型不匹配的问题只能在运行时触发,排查成本高
  • 开发时没有智能提示,写代码效率低
  • 数据传递没有明确契约,Controller传了什么、视图需要什么数据,仅看Action方法签名完全无法判断,后续维护的人需要同时翻Controller和View代码才能理清楚数据流向
  • 只有在传递零散的、全局通用的简单数据(比如页面标题、当前登录用户昵称这类)时,用ViewBag/ViewData才是性价比更高的选择,像下拉选项这类和页面业务强绑定的数据,用强类型视图模型的可维护性要高得多。

你原来代码里直接给asp-for="Warehouse"绑定整个Warehouse实体的写法是错的,表单提交只能传递简单类型值,绑定外键ID才是正确做法,否则模型绑定会直接失败。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:01:10