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

EF Core如何为含多种关联关系的模型植入初始数据?

EF Core InMemoryDb 种子数据关联配置问题解决

实体模型定义

public class Character
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string RpgClass { get; set; } = string.Empty;
    
    [JsonIgnore]
    public virtual User User { get; set; }
    [ForeignKey(nameof(User))]
    public int UserId { get; set; }

    public virtual Weapon Weapon { get; set; }
    [ForeignKey(nameof(Weapon))]
    public int WeaponId { get; set; }

    public virtual ICollection<Skill> Skills { get; set; }
}

public class Weapon
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int Damage { get; set; }

    [JsonIgnore]
    public virtual Character Character { get; set; }
    [ForeignKey(nameof(Character))]
    public int CharacterId { get; set; }
}

public class Skill
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int Damage { get; set; }
    
    [JsonIgnore]
    public virtual ICollection<Character> Characters { get; set; }
}

public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Username { get; set; } = string.Empty;
    
    public virtual ICollection<Character> Characters { get; set; }
}

错误提示

"The seed entity for entity type 'Character' cannot be added because it has the navigation 'Weapon' set. To seed relationships, add the entity seed to 'Weapon' and specify the foreign key values {'CharacterId'}."

当前OnModelCreating代码

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    List<Character> characters = ParseJson<Character>("./SeedData/characters.json");
    List<Skill> skills = ParseJson<Skill>("./SeedData/skills.json");
    List<User> users = ParseJson<User>("./SeedData/users.json");
    List<Weapon> weapons = ParseJson<Weapon>("./SeedData/weapons.json");

    modelBuilder.Entity<Character>()
        .HasOne(e => e.Weapon)
        .WithOne(e => e.Character)
        .HasForeignKey<Weapon>(e => e.CharacterId)
        .IsRequired();

    modelBuilder.Entity<Character>()
        .HasMany(e => e.Skills)
        .WithMany(e => e.Characters);

    modelBuilder.Entity<User>()
        .HasMany(e => e.Characters)
        .WithOne(e => e.User)
        .HasForeignKey(e => e.UserId)
        .IsRequired();

    modelBuilder.Entity<Weapon>().HasData(weapons);
    modelBuilder.Entity<Character>().HasData(characters);
    modelBuilder.Entity<Skill>().HasData(skills);
    modelBuilder.Entity<User>().HasData(users);
}

问题原因

EF Core种子数据时不允许直接设置导航属性对象,必须通过外键字段关联实体。你的Character种子数据里大概率包含了Weapon的完整对象,或者JSON解析时自动填充了Weapon导航属性,触发了EF的校验规则。

解决方案

1. 修正JSON种子数据格式

确保各实体的JSON只保留基础字段和外键,不要包含关联的导航对象:

  • users.json 示例:
[
  {"Id": 1, "Username": "玩家1"},
  {"Id": 2, "Username": "玩家2"}
]
  • characters.json 示例(只留UserId和WeaponId,去掉Weapon对象):
[
  {"Id": 1, "Name": "战士", "RpgClass": "Warrior", "UserId": 1, "WeaponId": 1},
  {"Id": 2, "Name": "法师", "RpgClass": "Mage", "UserId": 1, "WeaponId": 2}
]
  • weapons.json 示例(通过CharacterId关联角色):
[
  {"Id": 1, "Name": "铁剑", "Damage": 20, "CharacterId": 1},
  {"Id": 2, "Name": "法杖", "Damage": 15, "CharacterId": 2}
]
  • skills.json 示例:
[
  {"Id": 1, "Name": "猛击", "Damage": 30},
  {"Id": 2, "Name": "火球术", "Damage": 25}
]

2. 调整OnModelCreating逻辑

  • 按依赖顺序插入种子数据:先插User,再Character,接着Weapon,最后Skill,因为外键依赖需要主实体先存在。
  • 手动配置多对多中间表的种子数据(EF Core不会自动生成多对多的种子,必须显式配置)。

修改后的代码:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // 解析JSON数据
    List<Character> characters = ParseJson<Character>("./SeedData/characters.json");
    List<Skill> skills = ParseJson<Skill>("./SeedData/skills.json");
    List<User> users = ParseJson<User>("./SeedData/users.json");
    List<Weapon> weapons = ParseJson<Weapon>("./SeedData/weapons.json");

    // 保留原关联配置
    modelBuilder.Entity<Character>()
        .HasOne(e => e.Weapon)
        .WithOne(e => e.Character)
        .HasForeignKey<Weapon>(e => e.CharacterId)
        .IsRequired();

    modelBuilder.Entity<Character>()
        .HasMany(e => e.Skills)
        .WithMany(e => e.Characters)
        // 显式指定多对多中间表名称,方便后续配置种子
        .UsingEntity(j => j.ToTable("CharacterSkill"));

    modelBuilder.Entity<User>()
        .HasMany(e => e.Characters)
        .WithOne(e => e.User)
        .HasForeignKey(e => e.UserId)
        .IsRequired();

    // 按依赖顺序插入种子
    modelBuilder.Entity<User>().HasData(users);
    modelBuilder.Entity<Character>().HasData(characters);
    modelBuilder.Entity<Weapon>().HasData(weapons);
    modelBuilder.Entity<Skill>().HasData(skills);

    // 配置多对多中间表的种子数据(示例:角色1关联技能1,角色2关联技能2)
    modelBuilder.Entity("CharacterSkill")
        .HasData(
            new { CharactersId = 1, SkillsId = 1 },
            new { CharactersId = 2, SkillsId = 2 }
        );
}

3. 关键注意事项

  • 所有实体的Id必须手动指定(虽然用了DatabaseGeneratedOption.Identity,但种子数据需要显式给Id,EF才能正确关联外键)。
  • 解析JSON时,确保序列化工具(比如Newtonsoft.Json或System.Text.Json)不会自动填充导航属性,可以通过配置忽略导航属性,或者JSON里根本不包含这些字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:19:52