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

ASP.NET Core 6中DropdownList选中值无法传递至控制器致模型验证失败

ASP.NET Core 6 DropDownListFor选中值无法传递到控制器的解决方法

问题描述

在ASP.NET Core 6项目中使用@Html.DropDownListFor控件让用户选择创建交易的客户,但选中的客户信息无法传递到控制器,导致模型状态验证不通过。刚接触ASP.NET Core MVC,相关代码如下:

原视图模型类

namespace MyShop.ViewModels
{
    public class CreateDealVM
    {
        public Deals deal { get; set; }
        public IEnumerable<SelectListItem> CustomerListVM { get; set; }
    }
}

原模型类

namespace MyShop.Models
{
    [Table("Deals")]
    public class Deals
    {
        [Key]
        [Display(Name = "ID")]
        public int dealId { get; set; }

        [ForeignKey("Customer")]
        [Display(Name = "Customer")]
        public Customer customer { get; set; }

        [Display(Name = "CustomerName")]
        public string? parentCustomerName { get; set; }

        [Display(Name = "product")]
        public DealTypeEnum product { get; set; }

        [Display(Name = "Date")]
        public DateTime saleDate { get; set; }

        [Display(Name = "Quantity")]
        public float quantity { get; set; }

        [Display(Name = "Price")]
        public float price { get; set; }
    }
}

原控制器代码

public IActionResult Create()
{
    CreateDealVM vm = new CreateDealVM();
    vm.CustomerListVM  = _context.Customers.Select(x => new SelectListItem { Value = x.customerId.ToString(), Text = x.customerName }).ToList();
    return View(vm);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind("dealId,customer,product,saleDate,quantity,price")] Deals deal, CreateDealVM vm)
{
    if (ModelState.IsValid)
    {
        try
        {
            vm.deal.customer = _context.Customers.Find(vm.CustomerListVM);
            vm.deal = deal;
            _context.Deals.Add(vm.deal);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        return View(vm);
    }
    else
    {
        var errors = ModelState.Select(x => x.Value.Errors)
            .Where(y => y.Count > 0)
            .ToList();
    }
    return View(vm);
}

原视图代码

@model MyShop.ViewModels.CreateDealVM
@{
    ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Deals</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">
                @Html.LabelFor(model => model.deal.customer, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownListFor(model => model.CustomerListVM,Model.CustomerListVM,"Select Customer", htmlAttributes: new { @class = "form-control"})
                </div>
            </div>
            <div class="form-group">
                <label asp-for="deal.product" class="control-label"></label>
                <select asp-for="deal.product" class="form-control"></select>
                <span asp-validation-for="deal.product" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.saleDate" class="control-label"></label>
                <input asp-for="deal.saleDate" class="form-control" />
                <span asp-validation-for="deal.saleDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.quantity" class="control-label"></label>
                <input asp-for="deal.quantity" class="form-control" />
                <span asp-validation-for="deal.quantity" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.price" class="control-label"></label>
                <input asp-for="deal.price" class="form-control" />
                <span asp-validation-for="deal.price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>

核心问题分析与修正步骤

1. 完善Deals模型,添加外键字段

EF Core中导航属性Customer需要对应的外键字段才能正确绑定和保存,修改Deals模型:

namespace MyShop.Models
{
    [Table("Deals")]
    public class Deals
    {
        [Key]
        [Display(Name = "ID")]
        public int dealId { get; set; }

        // 添加外键字段,对应Customer的主键
        [Display(Name = "Customer")]
        public int CustomerId { get; set; }

        // 导航属性,标注外键关联
        [ForeignKey("CustomerId")]
        public Customer customer { get; set; }

        [Display(Name = "CustomerName")]
        public string? parentCustomerName { get; set; }

        [Display(Name = "product")]
        public DealTypeEnum product { get; set; }

        [Display(Name = "Date")]
        public DateTime saleDate { get; set; }

        [Display(Name = "Quantity")]
        public float quantity { get; set; }

        [Display(Name = "Price")]
        public float price { get; set; }
    }
}

2. 修改视图模型,添加选中客户ID字段

视图模型需要存储用户选中的客户ID,用于接收下拉框提交的值:

namespace MyShop.ViewModels
{
    public class CreateDealVM
    {
        public Deals deal { get; set; }
        public IEnumerable<SelectListItem> CustomerListVM { get; set; }
        // 新增:存储选中的客户ID
        public int SelectedCustomerId { get; set; }
    }
}

3. 修正视图中的DropDownListFor绑定

将下拉框绑定目标改为SelectedCustomerId,而非选项列表本身:

@model MyShop.ViewModels.CreateDealVM
@{
    ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Deals</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">
                @Html.LabelFor(model => model.SelectedCustomerId, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    <!-- 绑定到SelectedCustomerId,选项列表使用CustomerListVM -->
                    @Html.DropDownListFor(model => model.SelectedCustomerId, Model.CustomerListVM, "Select Customer", htmlAttributes: new { @class = "form-control"})
                    <span asp-validation-for="SelectedCustomerId" class="text-danger"></span>
                </div>
            </div>
            <!-- 其他字段保持不变 -->
            <div class="form-group">
                <label asp-for="deal.product" class="control-label"></label>
                <select asp-for="deal.product" class="form-control"></select>
                <span asp-validation-for="deal.product" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.saleDate" class="control-label"></label>
                <input asp-for="deal.saleDate" class="form-control" />
                <span asp-validation-for="deal.saleDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.quantity" class="control-label"></label>
                <input asp-for="deal.quantity" class="form-control" />
                <span asp-validation-for="deal.quantity" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="deal.price" class="control-label"></label>
                <input asp-for="deal.price" class="form-control" />
                <span asp-validation-for="deal.price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>

4. 重构控制器Post方法逻辑

只接收视图模型CreateDealVM,通过选中的ID关联客户并保存:

public IActionResult Create()
{
    CreateDealVM vm = new CreateDealVM();
    // 初始化下拉选项列表
    vm.CustomerListVM = _context.Customers.Select(x => new SelectListItem 
    { 
        Value = x.customerId.ToString(), 
        Text = x.customerName 
    }).ToList();
    // 初始化Deal对象,避免视图空引用
    vm.deal = new Deals();
    return View(vm);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateDealVM vm)
{
    // 验证选中的客户ID是否有效
    if (!_context.Customers.Any(c => c.customerId == vm.SelectedCustomerId))
    {
        ModelState.AddModelError(nameof(vm.SelectedCustomerId), "请选择有效的客户");
    }

    if (ModelState.IsValid)
    {
        try
        {
            // 关联选中的客户到交易
            vm.deal.CustomerId = vm.SelectedCustomerId;
            _context.Deals.Add(vm.deal);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            ModelState.AddModelError("", "保存交易时发生错误,请重试");
        }
    }

    // 验证失败后重新加载下拉选项列表
    vm.CustomerListVM = _context.Customers.Select(x => new SelectListItem 
    { 
        Value = x.customerId.ToString(), 
        Text = x.customerName 
    }).ToList();
    return View(vm);
}

关键说明

  • 下拉框提交的是客户ID(数字/字符串),无法直接绑定到Customer对象,必须通过外键字段中转。
  • 控制器Post方法只需接收视图模型,避免同时接收多个实体类导致参数绑定混乱。
  • 验证失败后需重新加载下拉选项列表,防止视图中下拉框为空。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 01:55:20