简体   繁体   中英

How to use Bogus generate test data for one-to-one and many-to-many relationship in EF Core C#?

Bogus is a good tool to generate test data; however, I have two issues on using it for C# Entity Framework on .net 5.0

  1. For the many-to-many relationship - how to generate the data for middle table? my current CourseStudent table is empty at the moment.

  2. For the one-to-one relationship - I try to generate 100 sets of student data; however, how to generate exact 100 records for StudentAddresses? Current table has only 64 records.

在此处输入图像描述

using System.Collections.Generic;

namespace EFCore_CodeFirst.Model
{
    public class Course
    {
        public Course()
        {
            this.Students = new HashSet<Student>();
        }

        public int CourseId { get; set; }
        public string CourseName { get; set; }

        public virtual ICollection<Student> Students { get; set; } //many-to-many relationship
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace EFCore_CodeFirst.Model
{
    public class Student
    {
        public int StudentId { get; set; }

        //The RowVersion is used as concurrency token, ensuring that you get an exception if a row you are updating has changed since you queried it.
        [ConcurrencyCheck]  
        public string StudentName { get; set; }
        public DateTime? DateOfBirth { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }


        //fully defined relationship
        public int? GradeId { get; set; }
        public virtual Grade Grade { get; set; }  //one-to-many relationship

        [Timestamp]  //timestamp/rowversion - auto-generated by db when inserted/updated.
        public byte[] RowVersion { get; set; }

        public virtual ICollection<Course> Courses { get; set; }  //many-to-many relationship
        public virtual StudentAddress Address { get; set; }  //one-to-one relationship
    }
}
using Bogus;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace EFCore_CodeFirst.Model
{
    public class SchoolDBInitializer
    {
        public List<Grade> Grades { get; set; }
        public List<Course> Courses { get; set; }
        public List<Student> Students { get; set; }

        public List<StudentAddress> StudentAddresses { get; set; }

        public SchoolDBInitializer()
        {
            const int numToSeed = 100;

            var gId = 1;
            Grades = new List<Grade>()
            {
                new Grade(){ GradeId = gId++, GradeName = "Grade 7", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 7", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 7", Section = "C" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 8", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 8", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 8", Section = "C" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 9", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 9", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 9", Section = "C" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 10", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 10", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 10", Section = "C" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 11", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 11", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 11", Section = "C" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 12", Section = "A" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 12", Section = "B" },
                new Grade(){ GradeId = gId++, GradeName = "Grade 12", Section = "C" }
            };

            var cId = 1;
            Courses = new List<Course>()
            {
                new Course() { CourseId = cId++, CourseName = "Chinese" },
                new Course() { CourseId = cId++, CourseName = "English" },
                new Course() { CourseId = cId++, CourseName = "Math" },
                new Course() { CourseId = cId++, CourseName = "Computing" },
                new Course() { CourseId = cId++, CourseName = "Art" },
                new Course() { CourseId = cId++, CourseName = "Physics" },
                new Course() { CourseId = cId++, CourseName = "Chemistry" },
                new Course() { CourseId = cId++, CourseName = "Biology" },
                new Course() { CourseId = cId++, CourseName = "History" }
            };


            var sId = 1;
            var startDate = DateTime.ParseExact("20010101", "yyyyMMdd", CultureInfo.InvariantCulture);
            var endDate = DateTime.ParseExact("20091231", "yyyyMMdd", CultureInfo.InvariantCulture);
            Faker<Student> studentFaker = new Faker<Student>()
                .StrictMode(false)
                .UseSeed(1122)  //It's a good idea to set a specific seed to generate different result of each Faker
                .RuleFor(s => s.StudentId, f => sId++)
                .RuleFor(s => s.StudentName, f => f.Name.FirstName())
                .RuleFor(s => s.DateOfBirth, f => f.Date.Between(startDate, endDate))
                .RuleFor(s => s.Height, f => f.Random.Decimal(120.5m, 195.5m))
                .RuleFor(s => s.Weight, f => f.Random.Float(40.5f, 90.5f))
                .RuleFor(s => s.GradeId, f => f.PickRandom(Grades).GradeId)  //lookup existing value in Grades
              //.RuleFor(s => s.CourseId, f => f.PickRandom(Courses).CourseId)
                ;  

            Students = studentFaker.Generate(numToSeed);

            var saId = 1;
            Faker<StudentAddress> studentAddressFaker = new Faker<StudentAddress>()
                .StrictMode(false)
                .UseSeed(1122)  //It's a good idea to set a specific seed to generate different result of each Faker
                .RuleFor(s => s.StudentAddressId, f => saId++)
                .RuleFor(s => s.Address1, f => f.Address.StreetAddress())
                .RuleFor(s => s.Address2, f => f.Address.SecondaryAddress())
                .RuleFor(s => s.City, f => f.Address.City())
                .RuleFor(s => s.Zipcode, f => f.Address.ZipCode())
                .RuleFor(s => s.State, f => f.Address.State())
                .RuleFor(s => s.Country, f => f.Address.Country())
                .RuleFor(s => s.AddressOfStudentId, f => f.PickRandom(Students).StudentId);

            StudentAddresses = studentAddressFaker.Generate(numToSeed);
        }
    }
}
using Microsoft.EntityFrameworkCore;

namespace EFCore_CodeFirst.Model
{
    public class SchoolContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=SchoolDB;Trusted_Connection=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>()
                .HasOne<StudentAddress>(s => s.Address)
                .WithOne(ad => ad.Student)
                .HasForeignKey<StudentAddress>(ad => ad.AddressOfStudentId);  //one-to-one relationship

            SchoolDBInitializer schoolData = new SchoolDBInitializer();

            modelBuilder.Entity<Course>().HasData(schoolData.Courses);
            modelBuilder.Entity<Grade>().HasData(schoolData.Grades);
            modelBuilder.Entity<Student>().HasData(schoolData.Students);
            modelBuilder.Entity<StudentAddress>().HasData(schoolData.StudentAddresses);
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Grade> Grades { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<StudentAddress> StudentAddresses { get; set; }
    }
}
namespace EFCore_CodeFirst.Model
{
    public class StudentAddress
    {
        public int StudentAddressId { get; set; }
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string City { get; set; }
        public string Zipcode { get; set; }
        public string State { get; set; }
        public string Country { get; set; }

        //one-to-one relationship
        public int AddressOfStudentId { get; set; }
        public virtual Student Student { get; set; }
    }
}

在此处输入图像描述

using System.Collections.Generic;

namespace EFCore_CodeFirst.Model
{
    public class Grade
    {
        public int GradeId { get; set; }
        public string GradeName { get; set; }
        public string Section { get; set; }

        public virtual ICollection<Student> Students { get; set; }
    }
}

Change the code to use many-to-many join-table class, the problem is fixed.

using System.Collections.Generic;

namespace EFCore_CodeFirst.Model.School
{
    public class Course
    {
        //public Course()
        //{
        //    this.Students = new HashSet<Student>();
        //}
        public int CourseId { get; set; }
        public string CourseName { get; set; }

        public int? TeacherId { get; set; }
        public virtual Teacher Teacher { get; set; } //one-to-many relationship

        //public virtual ICollection<Student> Students { get; set; } //many-to-many relationship (without join-table class) 
        public virtual ICollection<StudentCourse> StudentCourses { get; set; } //many-to-many relationship (WITH join-table class)

    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace EFCore_CodeFirst.Model.School
{
    public class Student
    {
        public int StudentId { get; set; }

        //The RowVersion is used as concurrency token, ensuring that you get an exception if a row you are updating has changed since you queried it.
        [ConcurrencyCheck]  
        public string StudentName { get; set; }
        public DateTime? DateOfBirth { get; set; }
        public decimal Height { get; set; }
        public float Weight { get; set; }


        //fully defined relationship
        public int? GradeId { get; set; }
        public virtual Grade Grade { get; set; }  //one-to-many relationship

        public int? StandardId { get; set; }
        public virtual Standard Standard { get; set; }  //one-to-many relationship


        [Timestamp]  //timestamp/rowversion - auto-generated by db when inserted/updated.
        public byte[] RowVersion { get; set; }

        public virtual StudentAddress Address { get; set; }  //one-to-one relationship

        //public virtual ICollection<Course> Courses { get; set; }  //many-to-many relationship (without join-table class) 
        public virtual ICollection<StudentCourse> StudentCourses { get; set; } //many-to-many relationship (WITH join-table class)
    }
}
namespace EFCore_CodeFirst.Model.School
{
    // Optional for EF Core 5.0
    // without this class, the system generate 
    //     Table name: CourseStudent
    //     Column names: CoursesCourseId,    StudentsStudentId
    //
    // this class defines the column names of the join table (aka. bridging, junction or linking table)
    // this provides a reference class for pre-loading the fake test data
    // Typically, the join table contains just the entity key values of each side of the relationship.
    //
    //
    // The relationship also needs to be configured via the Fluent API in OnModelCreating() of DbContext
    public class StudentCourse
    {
        public StudentCourse(){}
        public StudentCourse(Student student, Course course)
        {
            StudentId = student.StudentId;
            Student = student;
            CourseId = course.CourseId;
            Course = course;
        }
        public int StudentId { get; set; }      // System generated name without this class: StudentsStudentId
        public Student Student { get; set; }

        public int CourseId { get; set; }       // System generated name without this class: CoursesCourseId
        public Course Course { get; set; }
    }
}

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>()
                .HasOne<StudentAddress>(s => s.Address)
                .WithOne(ad => ad.Student)
                .HasForeignKey<StudentAddress>(ad => ad.AddressOfStudentId);  //one-to-one relationship

            // many-to-many relationship
            modelBuilder.Entity<StudentCourse>()
                .HasKey(cs => new { cs.CourseId, cs.StudentId });
            modelBuilder.Entity<StudentCourse>()
                .HasOne(cs => cs.Student)
                .WithMany(s => s.StudentCourses)
                .HasForeignKey(s => s.StudentId);
            modelBuilder.Entity<StudentCourse>()
                .HasOne(cs => cs.Course)
                .WithMany(c => c.StudentCourses)
                .HasForeignKey(c => c.CourseId);

            SchoolDBInitializer schoolData = new SchoolDBInitializer();

            modelBuilder.Entity<Course>().HasData(schoolData.Courses);
            modelBuilder.Entity<Grade>().HasData(schoolData.Grades);
            modelBuilder.Entity<Standard>().HasData(schoolData.Standards);
            modelBuilder.Entity<Student>().HasData(schoolData.Students);
            modelBuilder.Entity<StudentAddress>().HasData(schoolData.StudentAddresses);
            modelBuilder.Entity<StudentCourse>().HasData(schoolData.StudentCourses);
            modelBuilder.Entity<Teacher>().HasData(schoolData.Teachers);
        }
            Faker<StudentCourse> studentCourseFaker = new Faker<StudentCourse>()
                .StrictMode(false)
                .UseSeed(5354)  //It's a good idea to set a specific seed to generate different result of each Faker
                .RuleFor(s => s.StudentId, f => f.PickRandom(Students).StudentId)  //lookup existing value in Grades
                .RuleFor(s => s.CourseId, f => f.PickRandom(Courses).CourseId);

            StudentCourses = studentCourseFaker.Generate(numToSeed)
                .GroupBy(c => new { c.StudentId, c.CourseId }).Select(c => c.FirstOrDefault()).ToList();  //remove duplicate

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM