一对多 案例:文章和评论的关系就是一对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Article { public long Id { get ; set ; } public string Title { get ; set ; } public string Content { get ; set ; } public List<Comment> Comments { get ; set ; } = new List<Comment>(); } public class Comment { public long Id { get ; set ; } public Article Article { get ; set ; } public string Message { get ; set ; } }
EF Core实体之间关系配置采用HasXXX(...).WithYYY(...)
可以认为这是固定用法
HasXXX
代表当前实体类和关联的另一个实体类的关系
WithYYY
代表另一实体类与当前实体类的关系
例如在A实体类中定义builder.HasOne(…).WithMany(…)代表A实体类对应一个B实体类,而B实体类对应多个A实体类
实体配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class CommentConfig : IEntityTypeConfiguration <Comment >{ public void Configure (EntityTypeBuilder<Comment> builder ) { builder.ToTable("T_Comments" ); builder.HasOne<Article>(c => c.Article).WithMany(a => a.Comments) .IsRequired(); builder.Property(c => c.Message).IsRequired().IsUnicode(); } }
生成两张表如下,注意在T_Comments表中生成了ArticleId外键,指向T_Articles中的Id
1 2 3 4 5 6 7 8 9 10 11 12 Article a1 = new Article(); a1.Title = "微软发布.NET 6大版本的首个预览" ; a1.Content = "微软昨日在一篇官网博客文章中宣布了 .NET 6 首个预览版本的到来。" ; Comment c1 = new Comment() { Message = "支持" }; Comment c2 = new Comment() { Message = "微软太牛了" }; Comment c3 = new Comment() { Message = "火钳刘明" }; a1.Comments.Add(c1); a1.Comments.Add(c2); a1.Comments.Add(c3); using TestDbContext ctx = new TestDbContext();ctx.Articles.Add(a1); await ctx.SaveChangesAsync();
关联数据的获取 Article a = ctx.Articles.Include(a => a.Comments).Single(a => a.Id == 1);
Include方法起到了关联查询作用,用它生成对其他关联实体的查询操作 使用Include不仅仅能够查询到id=1的文章,也能查询到该文章所对应的评论
1 2 3 4 5 6 Article a = ctx.Articles.Include(a => a.Comments).Single(a => a.Id == 1 ); Console.WriteLine(a.Title); foreach (Comment c in a.Comments){ Console.WriteLine(c.Id + ":" + c.Message); }
关系外键属性设置 像上面案例所示,会在“多端”的T_Comments表中自动生成外键,该外键不能在Comments中直接获取,因为Comments实体类中并没用定义外键。如果有单独获取外键的需求,可以增加一个long类型的ArticleId属性,然后再关系配置上使用HasForeignKey(c=>c.ArticleId)
1 2 3 4 5 6 7 8 9 10 class CommentConfig : IEntityTypeConfiguration <Comment >{ public void Configure (EntityTypeBuilder<Comment> builder ) { builder.ToTable("T_Comments" ); builder.HasOne<Article>(c => c.Article).WithMany(a => a.Comments) .IsRequired().HasForeignKey(c => c.ArticleId); builder.Property(c => c.Message).IsRequired().IsUnicode(); } }
单导航属性 上例子中,Article类中声明了comments属性,在Commennt类中声明了Article属性,可以通过任何一方获取对方信息,这叫做双向导航。
单想导航是只在“多端”声明导航属性,不用在“一端”声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class User { public long Id { get ; set ; } public string Name { get ; set ; } } class Leave { public long Id { get ; set ; } public User Requester { get ; set ; } public User? Approver { get ; set ; } public string Remarks { get ; set ; } public DateTime From { get ; set ; } public DateTime To { get ; set ; } public int Status { get ; set ; } }
配置类
1 2 3 4 5 6 7 8 9 10 11 class LeaveConfig : IEntityTypeConfiguration <Leave >{ public void Configure (EntityTypeBuilder<Leave> builder ) { builder.ToTable("T_Leaves" ); builder.HasOne<User>(l => l.Requester).WithMany(); builder.HasOne<User>(l => l.Approver).WithMany(); builder.Property(l => l.Remarks).HasMaxLength(1000 ).IsUnicode(); } }
插入数据
1 2 3 4 5 6 7 8 9 10 11 User u1 = new User { Name = "杨中科" }; Leave leave1 = new Leave(); leave1.Requester = u1; leave1.From = new DateTime(2021 , 8 , 8 ); leave1.To = new DateTime(2021 , 8 , 9 ); leave1.Remarks = "家里三套房拆迁,回家处理" ; leave1.Status = 0 ; using TestDbContext ctx = new TestDbContext();ctx.Users.Add(u1); ctx.Leaves.Add(leave1); await ctx.SaveChangesAsync();
一对一 一对一的关系,由于双方都是“平等”的关系,外键可以设立在任何一方,所以必须显式在一种一个实体类中声明外键属性
1 2 3 4 5 6 7 8 9 10 11 12 class OrderConfig : IEntityTypeConfiguration <Order >{ public void Configure (EntityTypeBuilder<Order> builder ) { builder.ToTable("T_Orders" ); builder.Property(o => o.Address).IsUnicode(); builder.Property(o => o.Name).IsUnicode(); builder.HasOne<Delivery>(o => o.Delivery).WithOne(d => d.Order) .HasForeignKey<Delivery>(d => d.OrderId); } }
多对多 实体类,学生和老师
1 2 3 4 5 6 7 8 9 10 11 12 class Student { public long Id { get ; set ; } public string Name { get ; set ; } public List<Teacher> Teachers { get ; set ; } = new List<Teacher>(); } class Teacher { public long Id { get ; set ; } public string Name { get ; set ; } public List<Student> Students { get ; set ; } = new List<Student>(); }
配置类,只需要在一个配置类中设定两者的关系
在一对一和一对多中,只需要在某个表中增加外键就可以,但是在多对多中,需要引入额外的数据库表来保存两张表之间的对应关系。
1 2 3 4 5 6 7 8 9 10 class StudentConfig : IEntityTypeConfiguration <Student >{ public void Configure (EntityTypeBuilder<Student> builder ) { builder.ToTable("T_Students" ); builder.Property(s => s.Name).IsUnicode().HasMaxLength(20 ); builder.HasMany<Teacher>(s => s.Teachers).WithMany(t => t.Students) .UsingEntity(j => j.ToTable("T_Students_Teachers" )); } }
插入和查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Student s1 = new Student { Name = "tom" }; Student s2 = new Student { Name = "lily" }; Student s3 = new Student { Name = "lucy" }; Student s4 = new Student { Name = "tim" }; Student s5 = new Student { Name = "lina" }; Teacher t1 = new Teacher { Name = "杨中" }; Teacher t2 = new Teacher { Name = "罗翔" }; Teacher t3 = new Teacher { Name = "刘晓艳" }; t1.Students.Add(s1); t1.Students.Add(s2); t1.Students.Add(s3); t2.Students.Add(s1); t2.Students.Add(s3); t2.Students.Add(s5); t3.Students.Add(s2); t3.Students.Add(s4); using TestDbContext ctx = new TestDbContext();ctx.AddRange(t1, t2, t3); ctx.AddRange(s1, s2, s3, s4, s5); await ctx.SaveChangesAsync();using Microsoft.EntityFrameworkCore;using TestDbContext ctx = new TestDbContext();foreach (var t in ctx.Teachers.Include(t => t.Students)){ Console.WriteLine($"老师{t.Name} " ); foreach (var s in t.Students) { Console.WriteLine($"---{s.Name} " ); } }