都有哪几种关系?
1vs多,多vs多
1. 概念or关系映射相关方法:
1) 基本套路:this.Has***(o=>o.AAA).With***() 当前这个表和AAA属性的表关系是Has定义,With定义AAA表和这个表的关系
BBBConfig{ //这是1vs多的关系,1个AAA钟包含多个BBB public BBBConfig(){ //这个BBB有多少个A属性;从后读A钟有多少B this.Has***(o=>o.A).With***(); } } class AAA{ public long Id{get;set;} } class BBB{ public long Id{get;set;} public AAA A{get;set;} }
2) HasOptional() 有一个可选的(可以为空的)
3) HasRequired() 有一个必须的(不能为空的)
4) HasMany() 有很多的
5) WithOptional() 可选的
6) WithRequired() 必须的
7) WithMany() 很多的
Eg:
在AAA实体中配置this.HasRequired(o=>o.BBB).WithMany();
表示在AAA中有且只有一个BBB,而在BBB中有对应多个AAA;
在AAA实体中配置this.HasRequired(o=>o.BBB).WithRequired();
表示在AAA中有且只有一个BBB,而在BBB中也只有一个对应的AAA
练习:
public class Class { public long Id { get; set; } public string Name { get; set; } //双向设计关系会让模型变的更难理解,同时可通过代码替代 // Class clz = ctx.Classes.First(); //var students =ctx.Students.Where(s => s.ClassId == clz.Id); public virtual ICollection<Student> Students { get; set; } = new List<Student>(); } public class Student { public long Id { get; set; } public string Name { get; set; } public long ClassId { get; set; } public virtual Class Class { get; set; } public int Age { get; set; } }
public class ClassConfig:EntityTypeConfiguration<Class> { public ClassConfig() { ToTable("T_Classes"); } } public class StudentConfig:EntityTypeConfiguration<Student> { public StudentConfig() { ToTable("T_Students"); } }
public class TestContent:DbContext { public TestContent() : base("name=connStr") { Database.SetInitializer<TestContent>(null); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); } public DbSet<Class> Classes { get; set; } public DbSet<Student> Students { get; set; } }
static void Main(string[] args) { using (TestContent ctx = new Tests.TestContent()) { ctx.Database.Log = (sql) => { Console.WriteLine(sql); }; Class clz = new Tests.Class(); clz.Name = "一年一班"; ctx.Classes.Add(clz); Student s1 = new Tests.Student(); s1.Name = "小明"; s1.Class = clz; s1.Age = 18; ctx.Students.Add(s1); ctx.SaveChanges(); } }
FluentAPI关系配置深入:
1. 建议在多的一端进行配置,从数据库角度外键也是建在多的一端
public class StudentConfig:EntityTypeConfiguration<Student> { public StudentConfig() { ToTable("T_Students"); //许多个学生只有一个班级,一个班级有需要个学生,外键是classId HasRequired(e => e.Class).WithMany().HasForeignKey(e => e.ClassId); } //如果 ClassId 可空,那么就要写成 : //this.HasOptional (s => s.Class).WithMany().HasForeignKey(s => s.ClassId) }
2. 一对多的关系在一端配置就可以了,当然两边都配也不错。
3. 如果一张表中有两个指向另外一个表的外键怎么办?比如学生有“正常班级 Class”(不能空)和“小灶班级 XZClass”(可以空)两个班。如果用默认约定就会报错,怎么办?
this.HasRequired(s => s.Class).WithMany().HasForeignKey(s => s.ClassId); this. HasOptional (s => s.XZClass).WithMany().HasForeignKey(s => s.XZClassId);
多VS多关系配置:
class Student { public long Id { set; get; } public string Name { get; set; } public virtual ICollection<Teacher> Teachers { get; set; }=new List<Teacher>(); } class Teacher { public long Id { set; get; } public string Name { get; set; } public virtual ICollection<Student> Students { get; set; }=new List< Student >(); }
class StudentConfig : EntityTypeConfiguration<Student> { public StudentConfig() { ToTable("T_Students"); } } class TeacherConfig : EntityTypeConfiguration<Teacher> { public TeacherConfig() { ToTable("T_Teachers"); //多个老师对应多个学生,多个学生对应多个老师;map映射关系表, //当前左键指的当前表 this.HasMany(e => e.Students).WithMany(e => e.Teachers) .Map(m => m.ToTable("T_TeacherStudentRelations").MapLeftKey("TeacherId").MapRightKey("StudentId")); } }
这样不用中间表建实体(也可以为中间表建立一个实体,其实思路更清晰),就可以完
成多对多映射。当然如果中间关系表还想有其他字段,则要必须为中间表建立实体类。