上一篇文章简单介绍了efcore查询的流程,在这篇文章会延续原项目,添加新的关系类型( •̀ ω •́ )。
在数据库中,常见的关系类型有三种:
1 对 1 例如:一个班级 有 一个班主任
1 对 n 例如: 一个班级 有n个学生
n对n 例如:一个学生可以选择多门课程,一门课程可以被多名学生选择
这里n 对 n关系比较难理解一点,需要结合具体的上下文分析,这里也是不使用原项目中的产品与订单来解释的原因。
1 对 1 关系的数据库实现:
1to1.PNG
从技术上看,这是1对0或1关系,首先A是一定存在的,B有可能存在,A拥有B。
.net 中的对象关系实现
/// <summary>
/// 班主任
/// </summary>
[Table("class_teacher")]
public class ClassTeacher
{
public virtual int ClassTeacherId { get; set; }
/// <summary>
///
/// </summary>
[StringLength(20)]
public virtual string TeacherName { get; set; }
}
/// <summary>
/// 班级
/// </summary>
[Table("school_class")]
public class SchoolClass
{
public virtual int SchoolClassId { get; set; }
/// <summary>
/// 班级名称
/// </summary>
[StringLength(20)]
public virtual int ClassName { get; set; }
/// <summary>
/// 1对1关系
/// </summary>
public virtual ClassTeacher ClassTeacher { get; set; }
}
1 对 n 关系的数据库实现:
1 对n关系同 1对1关系在数据库的实现上是一样的,都是这里通过B持有A的主键作为外键实现这种关系,A拥有B,区别是A拥有B的个数。
1对n.PNG
.net 中的对象关系实现
[Table("student")]
public class Student
{
public virtual int StudentId { get; set; }
/// <summary>
///
/// </summary>
[StringLength(20)]
public virtual string StudentName { get; set; }
}
/// <summary>
/// 班级
/// </summary>
[Table("school_class")]
public class SchoolClass
{
public virtual int SchoolClassId { get; set; }
/// <summary>
/// 班级名称
/// </summary>
[StringLength(20)]
public virtual int ClassName { get; set; }
/// <summary>
/// 1对1关系
/// </summary>
public virtual ClassTeacher ClassTeacher { get; set; }
}
1 对 n 关系的数据库实现:
n 对 n 关系是无法用两张表来直接描述的,需要借助一张中间表。如上面的解释 n对n :一个学生可以选择多门课程,一门课程可以被多名学生选择,n对n 通过1 对 n 与 n对1 来描述。
n对n.PNG
.net 中的对象关系实现
/// <summary>
/// 中间表
/// </summary>
[Table("student_course")]
public class StudentCourse
{
public virtual int CourseId { get; set; }
public virtual int StudentId { get; set; }
/// <summary>
/// 1对1
/// </summary>
public virtual Student Student { get; set; }
/// <summary>
/// 1对1
/// </summary>
public virtual Course Course { get; set; }
}
[Table("student")]
public class Student
{
public virtual int StudentId { get; set; }
/// <summary>
///
/// </summary>
[StringLength(20)]
public virtual string StudentName { get; set; }
/// <summary>
/// 1对n
/// </summary>
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
[Table("course")]
public class Course
{
public virtual int CourseId { get; set; }
/// <summary>
///
/// </summary>
[StringLength(20)]
public virtual string CourseName { get; set; }
/// <summary>
/// 1对n
/// </summary>
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
添加DbSet 并添加OnModelCreating方法指定StudentCourse的主键,StudentCourse为复合主键,直接生成迁移文件会报错,需要使用fluent API方式添加。这里只添加n对n关系结构所以ClassTeacher与SchoolClass表就不加进来了。
public class AppDbContext:DbContext
{
private static string ConnStr;
static AppDbContext()
{
var builder = new ConfigurationBuilder();
builder.AddJsonFile("appsetting.json");
var config = builder.Build();
ConnStr = config["ConnectionString"];
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(ConnStr);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentCourse>()
.HasKey(x => new { x.StudentId, x.CourseId });
}
public DbSet<Products> Products { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
}
添加迁移文件,执行迁移
migration-2.PNG
查看生成的StudentCourse表结构:
student_course_table.PNG
基本与预期结构一致,从KEY IX_student_course_CourseId
(CourseId
) 知道添加了一个普通索引CourseId ,这个其实可以不需要,可以在执行迁移前在生成的迁移文件中修改。