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

C# ViewModel表单验证失败时创建数据库记录遇Null错误

表单验证失败引发NullReferenceException的修复方案

问题概述

表单验证失败时触发NullReferenceException空引用错误,视图初始加载正常,但验证失败返回视图时程序无法找到所需数据,同时数据保存的实现方式存在冗余和潜在风险。

错误原因分析

  1. 空引用错误根源:Post动作中验证失败后,虽然创建了新的ItemAndItemCategoriesViewModel并赋值了ItemCategories,但实际返回的是表单提交过来的viewModel——这个对象的ItemCategories是null(表单不会回传该集合),导致视图遍历Model.ItemCategories时触发空引用。
  2. 保存逻辑问题:手动复制所有属性冗余且容易出错;直接绑定ItemCategory对象而非外键,易导致绑定异常;未检查分类ID的有效性,可能引发数据库操作错误。

修复方案

1. 修复空引用错误

修改Post动作,验证失败时直接给传入的viewModel赋值ItemCategories,而非创建新对象:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ItemAndItemCategoriesViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        // 后续保存逻辑(见下文优化)
    }

    // 关键:将分类集合赋值给当前viewModel,而非新建对象
    viewModel.ItemCategories = _itemCategoriesService.getAllCategories();
    return View(viewModel);
}

2. 优化数据保存逻辑

步骤1:为Item模型添加显式外键

在Item类中添加外键字段,让EF Core更清晰地处理导航属性,同时简化表单绑定:

[Index(nameof(ItemCode), IsUnique = true)]
public class Item
{
    [Key]
    public int Id { get; set; }
    public string ItemCode { get; set; }
    public string ItemDescription { get; set; }
    public double Cost { get; set; }
    public double SalePrice { get; set; }
    public double Micrones { get; set; }
    public double Density { get; set; }
    
    public DateTime CreatedAt { get; set; } = DateTime.Now;
    
    // 新增外键字段,用于表单绑定
    [DisplayName("Item Category")]
    public int ItemCategoryId { get; set; }
    
    // 导航属性,通过外键关联
    [ForeignKey(nameof(ItemCategoryId))]
    public ItemCategory ItemCategory { get; set; }
}

步骤2:修改视图中的下拉框绑定

将下拉框绑定到ItemCategoryId而非整个ItemCategory对象:

<div class="mb-3">
    <label asp-for="Item.ItemCategoryId"></label>
    <select class="form-control" asp-for="Item.ItemCategoryId">
        @foreach (var category in Model.ItemCategories)
        {
            <option value="@category.Id">@category.CategoryName</option>
        }
    </select>
    <span asp-validation-for="Item.ItemCategoryId" class="text-danger"></span>
</div>

步骤3:简化Post动作的保存逻辑

去掉冗余的属性复制,添加分类有效性检查:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ItemAndItemCategoriesViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        // 检查选中的分类是否存在,避免无效ID
        if (!_itemCategoriesService.Exists(viewModel.Item.ItemCategoryId))
        {
            ModelState.AddModelError(nameof(viewModel.Item.ItemCategoryId), "Invalid category selected");
            viewModel.ItemCategories = _itemCategoriesService.getAllCategories();
            return View(viewModel);
        }
        
        // 直接使用viewModel中的Item对象,无需手动复制属性
        viewModel.Item.CreatedAt = DateTime.Now; // 确保时间正确(也可通过EF配置自动生成)
        _db.Items.Add(viewModel.Item);
        _db.SaveChanges();

        TempData["success"] = "Item created successfully";
        return RedirectToAction("Index");
    }

    viewModel.ItemCategories = _itemCategoriesService.getAllCategories();
    return View(viewModel);
}

注:需在_itemCategoriesService中添加Exists(int id)方法,用于检查分类是否存在。

修复效果

  • 验证失败返回视图时,ItemCategories会被正确赋值,不再触发空引用错误;
  • 表单绑定更简洁,避免复杂对象绑定的潜在问题;
  • 保存逻辑更简洁,减少冗余代码,同时增加了有效性检查,提升稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 10:55:56