use bogus to seed data in Entity Framework Core. with foreign key?

326 Views Asked by At

I used bogus to seed data in Entity Framework Core, but I get a foreign key constraint error:

SqliteException: SQLite Error 19: 'UNIQUE constraint failed: Vaccines.Id'.

This is my data generation class:

public static class DataGenerator
{
    public static List<DailyImmunization> GenerateDailyImmunizations(int count)
    {
        using var context = new IvbContext();
        context.Vaccines.LoadAsync();
        var vaccines = context.Vaccines.ToList();
        // Use the vaccineIds list as needed
        var dosageValues = new List<short> { 1, 2, 3 }; // Replace with actual dosage values

        var dailyImmunizations = new Faker<DailyImmunization>()
            .RuleFor(di => di.Id, f => f.Random.Guid())
            .RuleFor(di => di.Date, f => f.Date.PastDateOnly(2))
            .RuleFor(di => di.Vaccine, f => f.PickRandom(vaccines))
            .RuleFor(di => di.Dosage, f => f.PickRandom(dosageValues))
            .Generate(count);

        return dailyImmunizations;
    }
}

And this is my class:

public class DailyImmunization
{
    public virtual Guid Id { get; set; }
    public virtual DateOnly Date { get; set; }
    public virtual Guid VaccineId { get; set; }
    public virtual short Dosage { get; set; }

    public virtual required Vaccine Vaccine { get; set; }
}

This is the method that I called to save data:

private void GenerateDailyImmunization()
{
   var dailyImmunizations = DataGenerator.GenerateDailyImmunizations(50);
   _context.DailyImmunizations.AddRange(dailyImmunizations);
   _context.SaveChanges();

}

If I change the RuleFor(di => di.Vaccine) to RuleFor(di => di.VaccineId), I still get that foreign key constraint error.

I think I have to insert both fields but they are given randomly how do I add the VaccineId of the same selected Vaccine.

Edit:

The issue in the mapping of tables if I delete them the seeding will work. But I can not see what is the issue in my mapping code. and I need the to restrict delete:-

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<Vaccine>().HasMany(x => x.DailyImmunizations).WithOne(op => op.Vaccine)
        .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();

    modelBuilder.Entity<DailyImmunization>().HasOne(x => x.Vaccine).WithMany(op => op.DailyImmunizations)
        .OnDelete(DeleteBehavior.Restrict).HasForeignKey(@"vaccineId").IsRequired();
}
2

There are 2 best solutions below

1
On

The error you are getting is caused by a violation of the unique constraint on the Vaccines.Id column. This means that you are trying to insert a duplicate value for this column, which is not allowed. The reason why this happens is that you are using the same list of vaccines from the context to generate random values for the Vaccine property of the DailyImmunization class. This means that you may end up with multiple DailyImmunization instances that reference the same Vaccine instance, which will cause a conflict when you try to save them to the database.

To avoid this, you need to make sure that each DailyImmunization instance has a unique VaccineId value that corresponds to an existing Vaccine instance in the database. One way to do this is to use the RuleFor(di => di.VaccineId) method instead of the RuleFor(di => di.Vaccine) method, and then use the Include method to specify a list of valid values for the VaccineId property. For example, you could do something like this:

using var context = new IvbContext();
context.Vaccines.LoadAsync();
var vaccineIds = context.Vaccines.Select(v => v.Id).ToList(); // Get a list of vaccine ids from the database

var dailyImmunizations = new Faker<DailyImmunization>()
    .RuleFor(di => di.Id, f => f.Random.Guid())
    .RuleFor(di => di.Date, f => f.Date.PastDateOnly(2))
    .RuleFor(di => di.VaccineId, f => f.Random.Include(vaccineIds)) // Use the Include method to pick a random vaccine id from the list
    .RuleFor(di => di.Dosage, f => f.PickRandom(dosageValues))
    .Generate(count);

This way, you will ensure that each DailyImmunization instance has a valid and unique VaccineId value that references an existing Vaccine instance in the database. You don’t need to set the Vaccine property explicitly, as it will be populated by Entity Framework Core when you load or query the data.

0
On

After reading Microsoft documentation the issue was that I made the map twice for each relation. I should choose one of these not both 1-

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

2-

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