当前位置:首页 > 技能相关 > .NET > 正文内容

EFCore:表之间映射关系与导航属性的解读

admin1年前 (2023-12-25).NET2860 修订时间:2023-12-25 22:22:50

1、前置数据与环境准备

需要安装前置NuGet包:Pomelo.EntityFrameworkCore.Mysql 、microsoft.entityframeworkcore、Microsoft.EntityFrameworkCore.Tools

1.1设计如下实体类

//学生信息表
public class Student
{
   public int StudentId { get; set; }
   public string Name { get; set; }
   public string Sex { get; set; }
   public int Age { get; set; }
}

//教师信息表    
public class Teacher
{
   public int TeacherId { get; set; }
   public string Name { get; set; }
   public string Title { get; set; }
}

//课程表
public class Course
{
   public int CourseId { get; set; }
   public string Name { get; set; }
   public Teacher Teacher { get; set; }
}

//学生家庭地址表
public class StudentAddress
{
   public int StudentAddressId { get; set; }
   public string Address { get; set; }
   public string City { get; set; }
}

创建新的项目EFStudent,新建文件夹 Models,并依据上述实体类,创建对应的类模型。

image.png

1.2 确认Student、Teacher、Course、StudentAddress各表之间的关系

1.2.1 Student

一个学生只能有一个固定的家庭地址留存,所以学生和地址是一对一的关系。学生可以选择多个课程,学生和课程是多对多的关系。Student修改如下:

//学生信息表
public class Student
{
   public int StudentId { get; set; }
   public string Name { get; set; }
   public string Sex { get; set; }
   public int Age { get; set; }
   
   //导航属性
   //一对一
   public StudentAddress Address { get; set; }
   //一对多
   public IList<Course> Courses { get; set; } = new List<Course>();
}

1.2.2 Teacher

一个老师可以教授多门课程,Teacher修改如下:

//教师信息表    
public class Teacher
{
   public int TeacherId { get; set; }
   public string Name { get; set; }
   public string Title { get; set; }
   
   //一对多
   public IList<Course> Courses { get; set; } = new List<Course>();
}

1.2.3 Course

一个学生可能选择多门课程,一个课程也对应多个学生,Course修改如下:

//课程表
public class Course
{
   public int CourseId { get; set; }
   public string Name { get; set; }
   public Teacher Teacher { get; set; }
   
   //一对多
   public IList<Student> Students { get; set; } = new List<Student>();
   
   
}

1.2.4 StudentAddress

一个学生只能有一个固定的家庭地址留存,一个地址对应一个学生,StudentAddress修改如下:

public class StudentAddress
{

    public int StudentAddressId { get; set; }
    public string Address { get; set; }
    public string City { get; set; }

    //导航属性
    public int StudentId { get; set; }
    public Student Student { get; set; }
}

1.3 创建DbContext类(EfDbContext)并配置相关对应关系

  public class EfDbContext : DbContext
    {
        public EfDbContext()
        {
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
          optionsBuilder.UseMySql("server=b.lifeiai.com;user id=EFStudent;password=lf123456;database=efstudent", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.6.50-mysql"));
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //设置Id自增
            modelBuilder.Entity<Student>().Property(x => x.StudentId).ValueGeneratedOnAdd();
            //设置姓名最大长度为50,字符为unicode,不能为空
            modelBuilder.Entity<Student>().Property(x => x.Name).HasMaxLength(50).IsUnicode().IsRequired();
            //设置性别最大长度为5 字符为Unicode,不能为空
            modelBuilder.Entity<Student>().Property(x => x.Sex).HasMaxLength(5).IsUnicode().IsRequired();
            //一对一只需要配置一个类就行了
            modelBuilder.Entity<Student>().HasOne(x => x.Address).WithOne(x => x.Student).HasForeignKey<StudentAddress>(ad => ad.StudentId);

            modelBuilder.Entity<Course>().Property(x => x.Name).HasMaxLength(50).IsUnicode();
            //课程一对一
            modelBuilder.Entity<Course>().HasOne(x => x.Teacher).WithMany(t => t.Courses);
            modelBuilder.Entity<Course>().HasMany(x => x.Students).WithMany(st => st.Courses);

            modelBuilder.Entity<Teacher>().Property(x => x.Name).HasMaxLength(50).IsUnicode();
            modelBuilder.Entity<Teacher>().Property(x => x.Title).HasMaxLength(50).IsUnicode();

            modelBuilder.Entity<StudentAddress>().Property(x => x.City).HasMaxLength(100).IsRequired().IsUnicode();
            modelBuilder.Entity<StudentAddress>().Property(x => x.Address).HasMaxLength(500).IsRequired().IsUnicode();
        }
        public DbSet<Student> Students { get; set; }
        public DbSet<Teacher> Teachers { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<StudentAddress> Addresses { get; set; }
    }

根据以上代码可以总结一些注意事项,内容如下:

image.png

<1>配置一对一关系时,仅配置一方即可;

<2>配置一对多关系时,从多的方面下手,采用HasOne(…).WithMany(…)的方法比较简单和容易理解;

<3>传统的关系数据库中,配置多对多的时候,我们需要第三方的中间表进行相关联;EFCore6.0以上时,只需要配置相关的导航属性后,设置HasMany(…).WithMany(…)进行配置,EFCore自己生成相关的中间表;

<4>在传统的关系数据库中,配置多对多的时候,我们需要第三方的中间表进行相关联,但是在EFCore6.0以上时,我们只需要配置相关的导航属性后,设置HasMany(…).WithMany(…)进行配置,EFCore自己生成相关的中间表。

2.新增一些测试数据

using (var dbContext = new EfDbContext())
            {

                Course course1 = new Course() { CourseId = 3001, Name = "高等数学" };
                Course course2 = new Course() { CourseId = 3002, Name = "计算机原理" };
                Course course3 = new Course() { CourseId = 3003, Name = "操作系统原理" };
                Course course4 = new Course() { CourseId = 3004, Name = "编译原理" };


                Teacher teacher1 = new Teacher() { TeacherId = 10001, Name = "张教授", Title = "教授" };
                Teacher teacher2 = new Teacher() { TeacherId = 10002, Name = "王讲师", Title = "讲师" };

                teacher1.Courses = new Course[] { course1, course2 };
                teacher2.Courses = new Course[] { course3, course4 };

                dbContext.Teachers.AddRange(teacher1, teacher2);

                StudentAddress Address1 = new StudentAddress()
                {
                    StudentAddressId = 37001,
                    Address = "北京朝阳区",
                    City = "北京",
                };

                StudentAddress Address2 = new StudentAddress()
                {
                    StudentAddressId = 37002,
                    Address = "上海徐汇区",
                    City = "上海",
                };

                StudentAddress Address3 = new StudentAddress()
                {
                    StudentAddressId = 37003,
                    Address = "广州白云区",
                    City = "广州",
                };



                Student student1 = new Student()
                {
                    StudentId = 2022001,
                    Name = "王二小",
                    Age = 19,
                    Sex = "男",
                    Address = Address1,
                    Courses = new Course[] { course1, course2, course3 },

                };

                Student student2 = new Student()
                {
                    StudentId = 2022002,
                    Name = "张小五",
                    Age = 20,
                    Sex = "男",
                    Address = Address2,
                    Courses = new Course[] { course1, course2 },
                };

                Student student3 = new Student()
                {
                    StudentId = 2022003,
                    Name = "刘小花",
                    Age = 20,
                    Sex = "女",
                    Address = Address3,
                    Courses = new Course[] { course1, course3 },

                };
                dbContext.Students.AddRange(student1, student2, student3);

                dbContext.SaveChanges();
            }

上述代码部分解析:

AddRange()是批量添加,如上所示,在上面的例子中,我们只需要根据对应关系添加数据,在dbContext里添加对应关系的一方的就可以了,打开数据库后,可以发现数据顺利添加在其中。例如上面我们并没有调用dbContext.Addresses.Add()等方法,但是地址信息还是顺利写入了地址表。

2.1 查询数据

var dbContext = new EfDbContext();
var students= dbContext.Students.ToList();
foreach (var st in students)
{
    Console.WriteLine($"StudentId:{st.StudentId},Name:{st.Name},City:{st.Address.City}, Address:{st.Address.Address}");
}

上述方法会报错,这是因为st.Address.Address并没有实际的数据,需要通过Include方法进行相关的导航属性加载,代码修改如下:

var dbContext = new EfDbContext();
var students= dbContext.Students.Include(x=>x.Address).Include(x=>x.Courses).ToList();
foreach (var st in students)
{
   Console.WriteLine($"StudentId:{st.StudentId},Name:{st.Name},City:{st.Address.City}, Address:{st.Address.Address},Courses:{string.Join(",",st.Courses.Select(x=>x.Name))}");
}

image.png

Include可以连续包含多个属性,例如:

dbContext.Students.Include(x=>x.Address).Include(x=>x.Courses).ToList();

2.2 删除数据

using (var dbContext = new EfDbContext())
{
    var st = dbContext.Students.Single(x => x.StudentId == 2022001);
    dbContext.Remove(st);
    dbContext.SaveChanges();
}

image.png

image.png

将学号为2022001的学生删除后,发现相关的地址信息,以及选择的课程信息也被删除了,这是由于在设置的EFCore默认了删除的行为为级联的方式。

 您阅读本篇文章共花了: 

免责声明
本站内容均为博客主本人日常使用记录的存档,如侵犯你的权益请联系:lifei@zaiheze.com 546262132@qq.com 沟通删除事宜。本站仅带访问端口形式使用,已杜绝搜索引擎爬取。

扫描二维码推送至手机访问。

版权声明:本文由LIFEI - blog发布,如需转载请注明出处。

本文链接:http://www.lifeiai.com/?id=348

分享给朋友:

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。