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

Entity Code-First自引用一对多关系添加分类时出现验证错误

问题:Entity Framework Code-First自引用一对多关系添加分类时出现必填字段错误

我正在使用Entity Code-First开发操作MySQL数据库的API。我有一个Category类,它可包含多个子分类且仅有一个父分类,这是我首次使用自引用一对多关系,添加分类时遇到了问题。

现有代码

Category.cs

public class Category
{
    [Key]
    [Column("id")]
    public int Id { get; set; }
    [Column("parent_category_id")]
    public int? ParentCategoryId { get; set; }
    [Column("name")]
    public String Name { get; set; }
    [Column("description")]
    public string Description { get; set; }

    [ForeignKey("ParentCategoryId")]
    public virtual Category ParentCategory { get; set; }
    [ForeignKey("Id")]
    public virtual ICollection<Category> SubCategories { get; set; }
}

ShopDbContext.cs

public class ShopDbContext : DbContext
{
    public DbSet<Category> Category { get; set; }

    public ShopDbContext(DbContextOptions<ShopDbContext> options)
        : base(options) 
    {
        
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Category>()
            .HasOne(c => c.ParentCategory)
            .WithMany(c => c.SubCategories)
            .HasForeignKey(c => c.ParentCategoryId);
    }
}

CategoryRepository.cs

public class CategoryRepository : ICategoryRepository
{
    private readonly ShopDbContext _dbContext;

    public CategoryRepository(ShopDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<Category> AddCategoryAsync(Category category)
    {
        _dbContext.Category.Add(category);
        await _dbContext.SaveChangesAsync();

        return category;
    }
}

CategoryController.cs

[Route("api/v1/[controller]")]
[ApiController]
public class CategoryController : Controller
{
    private readonly ICategoryRepository _categoryRepository;

    public CategoryController(ICategoryRepository categoryRepository)
    {
        _categoryRepository = categoryRepository;
    }

    // POST: api/v1/Category
    [HttpPost]
    public async Task<IActionResult> AddCategory([FromBody] Category category)
    {
        await _categoryRepository.AddCategoryAsync(category);
        return CreatedAtAction("GetCategoryById", new { id = category.Id }, category);
    }
}

请求数据

{
  "parentCategoryId": 0,
  "name": "First Category",
  "description": "The first category, without parent"
}

错误信息

{
  "errors": {
    "SubCategories": [
      "The SubCategories field is required."
    ],
    "ParentCategory": [
      "The ParentCategory field is required."
    ]
  },
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "0HMPA9OFPSJK8:0000000F"
}

这两个字段不应为必填项,外键应从数据库填充对应数据。请问我的配置或注解哪里出错了?


解决方案

错误原因

  1. API模型验证问题:[ApiController]特性会自动启用模型状态验证,Category实体类中ParentCategory和SubCategories属性未标记为可空,导致模型绑定器判定这两个字段为必填项。
  2. 请求数据值错误:根分类的parentCategoryId应传null而非0,0会让EF尝试寻找ID为0的父分类,触发关联验证问题。

具体修改步骤

1. 调整Category实体类的导航属性

将ParentCategory改为可空引用类型,同时初始化SubCategories集合避免空引用:

public class Category
{
    [Key]
    [Column("id")]
    public int Id { get; set; }
    [Column("parent_category_id")]
    public int? ParentCategoryId { get; set; }
    [Column("name")]
    public string Name { get; set; }
    [Column("description")]
    public string Description { get; set; }

    [ForeignKey("ParentCategoryId")]
    public virtual Category? ParentCategory { get; set; } // 标记为可空引用类型
    [ForeignKey("Id")]
    public virtual ICollection<Category> SubCategories { get; set; } = new List<Category>(); // 初始化集合
}

2. 修正请求数据的parentCategoryId值

根分类无父分类,传递null:

{
  "parentCategoryId": null,
  "name": "First Category",
  "description": "The first category, without parent"
}

3. (推荐)使用DTO分离API模型与实体模型

不要直接用EF实体类作为API请求模型,创建专门的DTO避免导航属性干扰验证:

创建CreateCategoryDto.cs:

public class CreateCategoryDto
{
    public int? ParentCategoryId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

修改控制器逻辑:

[HttpPost]
public async Task<IActionResult> AddCategory([FromBody] CreateCategoryDto dto)
{
    var category = new Category
    {
        ParentCategoryId = dto.ParentCategoryId,
        Name = dto.Name,
        Description = dto.Description
    };
    await _categoryRepository.AddCategoryAsync(category);
    return CreatedAtAction("GetCategoryById", new { id = category.Id }, category);
}

额外说明

你的EF自引用关系配置是正确的,问题核心出在模型验证和请求数据的合理性上。初始化集合不仅能解决必填项问题,还能避免后续操作出现空引用异常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:17:52