• 4.4关系配置


    4.4关系配置

    一对多

    案例:文章和评论的关系就是一对多

    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; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    EF Core实体之间关系配置采用HasXXX(...).WithYYY(...)可以认为这是固定用法

    HasXXX代表当前实体类和关联的另一个实体类的关系

    WithYYY代表另一实体类与当前实体类的关系

    例如在A实体类中定义builder.HasOne(…).WithMany(…)代表A实体类对应一个B实体类,而B实体类对应多个A实体类

    实体配置类

    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();
            //HasOne
    (c => c.Article)表示Comment类中色Article属性指向Article属性 //WithMany(a => a.Comments)表示一个Article类对应多个Comment类,并且通过Article中的Comments属性 //进行访问 builder.Property(c => c.Message).IsRequired().IsUnicode(); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    生成两张表如下,注意在T_Comments表中生成了ArticleId外键,指向T_Articles中的Id
    在这里插入图片描述
    在这里插入图片描述

    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);//只需要加入Article就可以,会自动增加Comment对象到数据库
    await ctx.SaveChangesAsync();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    关联数据的获取

    Article a = ctx.Articles.Include(a => a.Comments).Single(a => a.Id == 1);

    Include方法起到了关联查询作用,用它生成对其他关联实体的查询操作

    关系外键属性设置

    像上面案例所示,会在“多端”的T_Comments表中自动生成外键,该外键不能在Comments中直接获取,因为Comments实体类中并没用定义外键。如果有单独获取外键的需求,可以增加一个long类型的ArticleId属性,然后再关系配置上使用HasForeignKey(c=>c.ArticleId)

    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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    单导航属性

    上例子中,Article类中声明了comments属性,在Commennt类中声明了Article属性,可以通过任何一方获取对方信息,这叫做双向导航。

    单想导航是只在“多端”声明导航属性,不用在“一端”声明

    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
    • 12
    • 13
    • 14
    • 15
    • 16

    配置类

    class LeaveConfig : IEntityTypeConfiguration<Leave>
    {
    	public void Configure(EntityTypeBuilder<Leave> builder)
    	{
    		builder.ToTable("T_Leaves");
            //WithMany()没有传递参数,因为User类中没有对应Leave类的属性
    		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);//如果单独插入u1,则不会插入leave1,因为user中没有leave的属性
    ctx.Leaves.Add(leave1);//如果单独插入leave也可以,会自动插入u1,因为有指向User的属性
    await ctx.SaveChangesAsync();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    一对一

    一对一的关系,由于双方都是“平等”的关系,外键可以设立在任何一方,所以必须显式在一种一个实体类中声明外键属性

    //案例:订单及物流信息
    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);//物流类(Delivery)中的OrderId作为指向Order的外键
    	}
    }
    
    • 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
    • 11
    • 12

    配置类,只需要在一个配置类中设定两者的关系

    在一对一和一对多中,只需要在某个表中增加外键就可以,但是在多对多中,需要引入额外的数据库表来保存两张表之间的对应关系。

    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"));//使用UsingEntity来配置中间表T_Students_Teachers
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    插入和查询

    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);//是对Add的简化调用,仍然是一个一个加入
    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}");
    	}
    }
    
    • 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
  • 相关阅读:
    设计模式19-状态模式
    vue 项目打包性能分析插件 webpack-bundle-analyzer
    系分 - 系统分析
    【Java面试指北】Exception Error Throwable 你分得清么?
    6.SpringBoot与缓存
    触觉设备,临场感,预测控制,DOB
    【面试经典150 | 数组】删除有序数组中的重复项
    搞懂SpringBean生命周期与依赖注入:你还在为这些热门技术感到困惑吗?Comate插件来帮你解答!
    【OpenCV】在MacOS上源码编译OpenCV
    Least-upper-bound property
  • 原文地址:https://blog.csdn.net/weixin_44064908/article/details/126315539