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

ASP.NET Core:实现可复用且绑定Razor父模型的模块化多下拉框

问题

如何在ASP.NET Core中实现模块化可复用(可在同一页面多次使用)且能绑定到Razor父页面模型的下拉框?

我了解过单个下拉框的实现方式(类似官方文档中的示例),但现在需要多个可复用的下拉框(比如新增选择大学年级的下拉框:大一、大二、大三、大四)。但C#不支持多基类继承,无法通过继承多个下拉框基类来实现。另外,就算是单个下拉框,有没有比示例更好的实现方式?

以下是官方示例中的相关代码:

// Pages/Courses/DepartmentNamePageModel.cs
using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace ContosoUniversity.Pages.Courses
{
    public class DepartmentNamePageModel : PageModel
    {
        public SelectList DepartmentNameSL { get; set; }

        public void PopulateDepartmentsDropDownList(SchoolContext _context,
            object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                               orderby d.Name // 按名称排序
                               select d;

            DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
                nameof(Department.DepartmentID),
                nameof(Department.Name),
                selectedDepartment);
        }
    }
}

// Pages/Courses/Create.cshtml.cs
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class CreateModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            PopulateDepartmentsDropDownList(_context);
            return Page();
        }

        [BindProperty]
        public Course Course { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            var emptyCourse = new Course();

            if (await TryUpdateModelAsync<Course>(
                 emptyCourse,
                 "course",   // 表单值前缀
                 s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
            {
                 _context.Courses.Add(emptyCourse);
                 await _context.SaveChangesAsync();
                 return RedirectToPage("./Index");
            }

            // 如果验证失败,重新生成下拉框并保留选中值
            PopulateDepartmentsDropDownList(_context, emptyCourse.DepartmentID);
            return Page();
        }
      }
}

解决方案

针对多下拉框复用和单下拉框优化的需求,推荐以下几种实现方式:

方案1:工具类/服务封装下拉逻辑

把每个下拉框的数据获取逻辑封装成独立的服务类,避免使用继承,直接在PageModel中注入调用,支持多个下拉框的复用。

步骤1:创建下拉框服务类

public class DropDownService
{
    private readonly SchoolContext _context;

    public DropDownService(SchoolContext context)
    {
        _context = context;
    }

    // 生成系部下拉框数据
    public SelectList GetDepartmentSelectList(object selectedId = null)
    {
        var departments = _context.Departments
            .AsNoTracking()
            .OrderBy(d => d.Name);
            
        return new SelectList(departments, nameof(Department.DepartmentID), nameof(Department.Name), selectedId);
    }

    // 生成年级下拉框数据
    public SelectList GetGradeSelectList(object selectedId = null)
    {
        // 模拟年级数据,实际可从数据库/配置读取
        var grades = new List<SelectListItem>
        {
            new() { Value = "1", Text = "大一" },
            new() { Value = "2", Text = "大二" },
            new() { Value = "3", Text = "大三" },
            new() { Value = "4", Text = "大四" }
        };
        
        return new SelectList(grades, "Value", "Text", selectedId);
    }
}

步骤2:注册服务

在Program.cs中注册该服务:

builder.Services.AddScoped<DropDownService>();

步骤3:在PageModel中使用

public class CreateModel : PageModel
{
    private readonly SchoolContext _context;
    private readonly DropDownService _dropDownService;

    // 存储下拉框数据
    public SelectList DepartmentSL { get; set; }
    public SelectList GradeSL { get; set; }

    [BindProperty]
    public Course Course { get; set; }
    // 绑定年级选择值
    [BindProperty]
    public int GradeId { get; set; }

    public CreateModel(SchoolContext context, DropDownService dropDownService)
    {
        _context = context;
        _dropDownService = dropDownService;
    }

    public IActionResult OnGet()
    {
        // 初始化下拉框
        DepartmentSL = _dropDownService.GetDepartmentSelectList();
        GradeSL = _dropDownService.GetGradeSelectList();
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        var emptyCourse = new Course();

        if (await TryUpdateModelAsync<Course>(
             emptyCourse,
             "course",
             s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
        {
             emptyCourse.GradeId = GradeId;
             _context.Courses.Add(emptyCourse);
             await _context.SaveChangesAsync();
             return RedirectToPage("./Index");
        }

        // 验证失败时,重新生成下拉框并保留选中值
        DepartmentSL = _dropDownService.GetDepartmentSelectList(emptyCourse.DepartmentID);
        GradeSL = _dropDownService.GetGradeSelectList(GradeId);
        return Page();
    }
}

视图中渲染下拉框

<div class="form-group">
    <label asp-for="Course.DepartmentID"></label>
    <select asp-for="Course.DepartmentID" asp-items="Model.DepartmentSL" class="form-control"></select>
    <span asp-validation-for="Course.DepartmentID" class="text-danger"></span>
</div>

<div class="form-group">
    <label asp-for="GradeId"></label>
    <select asp-for="GradeId" asp-items="Model.GradeSL" class="form-control"></select>
    <span asp-validation-for="GradeId" class="text-danger"></span>
</div>

方案2:局部视图实现页面内多次复用

把下拉框封装成局部视图,通过ViewModel传递绑定字段和下拉数据,支持同一页面多次渲染不同绑定目标的下拉框。

步骤1:创建下拉框ViewModel

public class DropDownViewModel
{
    // 绑定的父模型属性名
    public string BindPropertyName { get; set; }
    // 下拉框选项列表
    public SelectList SelectList { get; set; }
}

步骤2:创建局部视图_DepartmentDropDown.cshtml

@model DropDownViewModel

<div class="form-group">
    <label asp-for="@Model.BindPropertyName"></label>
    <select asp-for="@Model.BindPropertyName" asp-items="@Model.SelectList" class="form-control"></select>
    <span asp-validation-for="@Model.BindPropertyName" class="text-danger"></span>
</div>

步骤3:在父页面中调用

@{
    // 第一个系部下拉框,绑定到Course.DepartmentID
    var courseDeptVm = new DropDownViewModel
    {
        BindPropertyName = "Course.DepartmentID",
        SelectList = Model.DepartmentSL
    };
    
    // 第二个系部下拉框,绑定到其他模型的DepartmentID
    var anotherDeptVm = new DropDownViewModel
    {
        BindPropertyName = "AnotherModel.DepartmentID",
        SelectList = Model.DepartmentSL
    };
}

<partial name="_DepartmentDropDown" model="courseDeptVm" />
<partial name="_DepartmentDropDown" model="anotherDeptVm" />

方案3:ViewComponent实现独立可复用组件

ViewComponent适合更复杂的可复用场景,自带独立的逻辑处理和视图,支持依赖注入,可在任意页面调用。

步骤1:创建ViewComponent类

public class DepartmentDropDownViewComponent : ViewComponent
{
    private readonly DropDownService _dropDownService;

    public DepartmentDropDownViewComponent(DropDownService dropDownService)
    {
        _dropDownService = dropDownService;
    }

    // 调用时传入绑定属性名和选中值
    public IViewComponentResult Invoke(string bindPropertyName, object selectedId = null)
    {
        var model = new DropDownViewModel
        {
            BindPropertyName = bindPropertyName,
            SelectList = _dropDownService.GetDepartmentSelectList(selectedId)
        };
        return View(model);
    }
}

步骤2:创建ViewComponent视图

在Views/Shared/Components/DepartmentDropDown/Default.cshtml中添加视图内容:

@model DropDownViewModel

<div class="form-group">
    <label asp-for="@Model.BindPropertyName"></label>
    <select asp-for="@Model.BindPropertyName" asp-items="@Model.SelectList" class="form-control"></select>
    <span asp-validation-for="@Model.BindPropertyName" class="text-danger"></span>
</div>

步骤3:在父页面中调用

@await Component.InvokeAsync("DepartmentDropDown", new { bindPropertyName = "Course.DepartmentID", selectedId = Model.Course?.DepartmentID })
@await Component.InvokeAsync("DepartmentDropDown", new { bindPropertyName = "AnotherModel.DepartmentID", selectedId = Model.AnotherModel?.DepartmentID })

单个下拉框的优化建议

即使只需要单个下拉框,也推荐使用工具类/服务的方式替代官方示例的继承方案,优势在于:

  • 避免PageModel的继承层级,代码结构更清晰
  • 下拉逻辑集中在服务类,便于统一维护和单元测试
  • 同一逻辑可在多个PageModel中复用,无需重复编写

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 22:37:33