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

使用Bogus为EF Core含外键数据播种时遇唯一约束错误

解决EF Core用Bogus播种时的SQLite唯一约束错误

错误根源

你遇到的UNIQUE constraint failed: Vaccines.Id错误,核心问题是跨DbContext的实体跟踪冲突:

  • DataGenerator里新建了临时IvbContext加载疫苗数据,这些疫苗实体属于这个临时上下文。
  • 当把生成的DailyImmunization添加到主上下文_context时,EF会把这些来自临时上下文的疫苗实体识别为新实体,尝试重新插入数据库,导致主键重复违反唯一约束。

另外你的映射配置里,DailyImmunization和Vaccine的关系被重复定义了两次,这也可能导致EF的关系元数据混乱。

修复步骤

1. 重构DataGenerator,避免跨上下文加载实体

把主上下文的疫苗列表传入生成方法,不要在方法内新建上下文:

public static class DataGenerator
{
    // 传入已加载的疫苗列表,而非自己创建上下文
    public static List<DailyImmunization> GenerateDailyImmunizations(int count, List<Vaccine> vaccines)
    {
        var dosageValues = new List<short> { 1, 2, 3 };

        var dailyImmunizations = new Faker<DailyImmunization>()
            .RuleFor(di => di.Id, f => f.Random.Guid())
            .RuleFor(di => di.Date, f => f.Date.PastDateOnly(2))
            // 只赋值VaccineId,避免引入外部上下文的实体
            .RuleFor(di => di.VaccineId, f => f.PickRandom(vaccines).Id)
            .RuleFor(di => di.Dosage, f => f.PickRandom(dosageValues))
            // 不需要赋值Vaccine导航属性,EF会通过VaccineId自动关联
            .Generate(count);

        return dailyImmunizations;
    }
}

2. 修改保存方法,传入主上下文的疫苗数据

从主上下文加载疫苗,再传给生成方法:

private void GenerateDailyImmunization()
{
    // 从当前上下文加载疫苗,确保实体属于当前跟踪上下文
    var vaccines = _context.Vaccines.ToList();
    var dailyImmunizations = DataGenerator.GenerateDailyImmunizations(50, vaccines);
    _context.DailyImmunizations.AddRange(dailyImmunizations);
    _context.SaveChanges();
}

3. 清理重复的关系映射配置

你的RelationshipsMapping方法里,DailyImmunization和Vaccine的关系被定义了两次,保留其中一组即可,去掉重复的那行避免EF元数据冲突:

修改后的映射代码:

private void RelationshipsMapping(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Vaccine>()
        .HasOne(x => x.VaccineType)
        .WithMany(op => op.Vaccines)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("VaccineTypeId")
        .IsRequired();

    modelBuilder.Entity<Vaccine>()
        .HasOne(x => x.TargetDisease)
        .WithMany(op => op.Vaccines)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("TargetDiseaseId")
        .IsRequired();

    modelBuilder.Entity<Vaccine>()
        .HasOne(x => x.RouteOfAdministration)
        .WithMany(op => op.Vaccines)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("RouteOfAdministrationId")
        .IsRequired();

    // 只保留这一组关系定义即可
    modelBuilder.Entity<DailyImmunization>()
        .HasOne(x => x.Vaccine)
        .WithMany(op => op.DailyImmunizations)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("vaccineId")
        .IsRequired();

    modelBuilder.Entity<VaccineType>()
        .HasMany(x => x.Vaccines)
        .WithOne(op => op.VaccineType)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("VaccineTypeId")
        .IsRequired();

    modelBuilder.Entity<TargetDisease>()
        .HasMany(x => x.Vaccines)
        .WithOne(op => op.TargetDisease)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("TargetDiseaseId")
        .IsRequired();

    modelBuilder.Entity<RouteOfAdministration>()
        .HasMany(x => x.Vaccines)
        .WithOne(op => op.RouteOfAdministration)
        .OnDelete(DeleteBehavior.Restrict)
        .HasForeignKey("RouteOfAdministrationId")
        .IsRequired();
}

额外说明

如果一定要赋值导航属性Vaccine,可以在保存方法里添加一行代码,标记疫苗实体为已存在,避免EF尝试插入:

// 告诉EF这些疫苗是已存在的,不需要重新插入
_context.AttachRange(vaccines);

但更推荐直接赋值VaccineId的方式,更简洁且避免跟踪问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:45:55