EFCore 从入门到精通-4(映射关系与导航属性)
1、准备条件1.1新建一个空的Asp.net core项目安装 Microsoft.EntityFrameworkCore.Tools安装 Pomelo.EntityFrameworkCore.MySql1.2 设计如下实体类public class Student{public int Id { get; set; }public string Name { get; set; }public
目录
1.初始准备
1.1 工具准备
开发软件:VisualStudio2022,EFCore6.0
.net Core版本:.Net6.0
数据库:mysql8.0
数据库管理软件:Navicat
1.2 新建一个控制台程序
并按照前面的学习内容,在程序包管理控制台里 安装 Microsoft.EntityFrameworkCore.Tools
安装 Pomelo.EntityFrameworkCore.MySql
1.3 设计如下实体类
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; }
}
2.实体对应关系
2.1 一对一
在上述实体关系中,一个学生只能由一个固定的地址,那么学生和地址是一对一的关系。而学生可以选择多个课程,学生和课程是多对多的关系。修改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>();
}
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; }
}
2.2 一对多
一个老师可以教授多门课程,老师和课程是一对多的关系。
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>();
}
2.3 多对多
一个学生可能选择多门课程,一个课程也可以被多个学生同时选择。修改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>();
}
2.3 在 DbContext类里配置相关对应关系
在项目中新建一个名为EFLearnDbContext,并继承于DbContext
public class EFLearnDbContext: DbContext
{
public EFLearnDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("server=192.168.0.106;database=EFCoreLearn;user=root;password=123456", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.28-mysql"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region 基本配置
modelBuilder.Entity<Student>().Property(x => x.StudentId).ValueGeneratedOnAdd();//设置Id自增
//设置姓名最大长度为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);
#endregion
#region Course
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);
#endregion
#region Teacher配置
modelBuilder.Entity<Teacher>().Property(x => x.Name).HasMaxLength(50).IsUnicode();
modelBuilder.Entity<Teacher>().Property(x => x.Title).HasMaxLength(50).IsUnicode();
#endregion
#region 配置地址
modelBuilder.Entity<StudentAddress>().Property(x=>x.City).HasMaxLength(100).IsRequired().IsUnicode();
modelBuilder.Entity<StudentAddress>().Property(x=>x.Address).HasMaxLength(500).IsRequired().IsUnicode();
#endregion
}
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<StudentAddress> Addresses { get; set; }
}
如上所示,我们总结如下:
- 在配置对应的时候,我们只配置一方就好了,另外一方不需要配置,即使你重复配置了也不会出错。
- 在配置一对多的时候,我们最好从多的方面下手,采用HasOne(…).WithMany(…)的方法比较简单和容易理解。
- 在传统的关系数据库中,配置多对多的时候,我们需要第三方的中间表进行相关联,但是在EFCore6.0以上时,我们只需要配置相关的导航属性后,设置HasMany(…).WithMany(…)进行配置,EFCore自己生成相关的中间表
- 建议在设置对应关系的时候,如果为集合,最后给个初值化值。例如:public IList Students { get; set; } = new List()
3.生成数据库,查询相关ER关系
在程序包管理控制台输入 Add-Migration Initial
然后输入 Update-Database
然后打开数据库查看相关数据表的ER关系图,
同样的在多对多关系中,Courses和Students 出现了一个新表为 coursestudent,表结构中有主外键关系约束,除此之外,在删除时是级联关系的。
4.体验CURD
4.1添加数据
在Program.cs里添加如下代码
//1.添加一些数据
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()等方法,但是地址信息还是顺利写入了地址表。
4.2查询数据
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}");
}
上述代码运行的时候,会报错提示NullReference,主要的原因是此时Address的属性为空,这说明在普通的查询中,导航属性并不能正确的加载。
将代码修改如下:
var students= dbContext.Students.Include(x=>x.Address).ToList();
foreach (var st in students)
{
Console.WriteLine($"StudentId:{st.StudentId},Name:{st.Name},City:{st.Address.City}, Address:{st.Address.Address}");
}
发现可以正确显示结果,所以在包含导航属性查询的时候,可以使用Include方法进行相关的导航属性加载。
Include可以连续包含多个属性,添加如下代码,我们将Address和Courses都查询出来
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))}");
}
4.3 删除数据
#region 删除数据
var st = dbContext.Students.Single(x => x.StudentId == 2022001);
dbContext.Remove(st);
dbContext.SaveChanges();
#endregion
如上图将学号为2022001的学生删除后,发现相关的地址信息,以及选择的课程信息也被删除了,这是由于在设置的EFCore默认了删除的行为为级联的方式。
更多推荐
所有评论(0)