EF Core关系配置

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在进行项目开发时,很少有一张数据库表是独立存在的,大部分数据表之间都是有关系的,这也是关系型数据库的体现

一、一对一

1.代码构成

实体之间的关系有很多,最常见就是一对一的关系了,现在我们通过一个实例来体会一下一对一是怎么进行配置的
在这里插入图片描述
上面是最终的项目结构

  • 首先是两个实体类
    class Delivery
    {
        public long Id { get; set; }    
        public string CompanyName { get; set; }

        public string Number { get; set; }

        public Order Order { get; set; }  

        public long OrderId { get; set; }    ///指向订单的外键
    }
    class Order
    {
        public long Id { get; set; }
        public string Name { get; set; }

        public string Address { get; set; }
        public Delivery? Delivery { get; set; }
    }
  • 然后是各自对应的Config配置类
    class DeliveryConfig : IEntityTypeConfiguration<Delivery>
    {
        public void Configure(EntityTypeBuilder<Delivery> builder)
        {
            builder.ToTable("T_Deliveries");
        }
    }
        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(o => o.Order).HasForeignKey<Delivery>(d => d.OrderId);
        }
    }
  1. 这里需要注意了,我们一对一的配置就在上面这些代码中,一开始创建的实体类时,public long OrderId { get; set; } //指向订单的外键,这里配置了一个外键,大家都知道外键是干啥的,因为是一对一,这个外键可以说在哪个类上配置都可以的,这里只是演示其中一种,而在Order实体类中有这样一个属性public Delivery? Delivery { get; set; },这个属性的意义就是一个订单对应一个快递信息,是快递Delivery的引用类型
  2. 而在配置类中,在OrderConfig中进行的一对一的配置
  3. builder.HasOne<Delivery>(o => o.Delivery).WithOne(o => o.Order).HasForeignKey<Delivery>(d => d.OrderId);的意义也很明确,代表:Order实体类中有一个Delivery属性,这个属性对应一个订单Order,并且在Delivery类中有一个外键OrderId

2.测试例子

完成上面的代码书写后,别忘了DbContext

    class MyDbContext:DbContext
    {
      
        public DbSet<Order> Orders { get; set; }
        public DbSet<Delivery> Deliveries { get; set; }



        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer
                ("Server=.;Database=demo4;Trusted_Connection=True;MultipleActiveResultSets=true;Encrypt=True;TrustServerCertificate=True;");
            optionsBuilder.LogTo(Console.WriteLine);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //从当前程序集中加载所有的IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }

执行Add-Migration命令,然后Update-database
这里提供一些测试例子

    class Program
    {
        static async Task Main(string[] args)
        {
            using(MyDbContext context =new MyDbContext())
            {
                Order order = new Order();

                order.Address = "某某市某某区";
                order.Name = "USB充电器";

                Delivery delivery = new Delivery();
                delivery.CompanyName = "蜗牛快递";

                delivery.Number = "SN1654921344";
                delivery.Order = order;

                context.Deliveries.Add(delivery);
                await context.SaveChangesAsync();
            }
        }
    }

二、一对多

使用一个文章Article对应多个评论 Comment来进行实例演示

1.代码构成

在这里插入图片描述

  • 实体类的代码
    class Article
    {
        public long Id { get; set; }
        public string Title { get; set; }
        public string Message { get; set; }

        public List<Comment> comments { get; set; } = new List<Comment>();  //建议给个空的List

    }
    class Comment
    {
        public long Id { get; set; }

        public string Message { get; set; }

        public Article TheArticle { get; set; }

        public long TheArticleId { get; set; }
    }
  • 实体类的配置
    class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles");
            builder.Property(a => a.Title).HasMaxLength(100).IsUnicode().IsRequired();
            builder.Property(a => a.Message).IsUnicode().IsRequired();

        }
    }
    class CommentConfig : IEntityTypeConfiguration<Comment>
    {
        public void Configure(EntityTypeBuilder<Comment> builder)
        {
            builder.ToTable("T_Comments");
            builder.Property(a => a.Message).IsUnicode().IsRequired();
            builder.HasOne<Article>(c => c.TheArticle).WithMany(a => a.comments).HasForeignKey(c=>c.TheArticleId).IsRequired();
            
        }
    }
  1. 在上面是一对多的配置类,我们可以看到在Article中,是正常配置的表名和属性限制的,我们是在多的一方,也就是评论Comment的配置类中进行一对多进行配置的
  2. builder.HasOne<Article>(c => c.TheArticle).WithMany(a => a.comments).HasForeignKey(c=>c.TheArticleId).IsRequired();意义很明显一个文章对应多个评论,并且在Comment中有一个外键TheArticleId

至于DbContext的配置,这里省略了,自行配置…

2.测试例子

    internal class Program
    {
        static void Main(string[] args)
        {
            using(MyDbContext context =new MyDbContext())
            {
                /*Article a1 = new Article();
                a1.Title= "杨中科被评为亚洲最有实力程序员";
                a1.Message = "据报道...";

                Comment c1 = new Comment { Message="太厉害了!"};
                Comment c2 = new Comment { Message="吹吧,SB!"};

                a1.comments.Add(c1);
                a1.comments.Add(c2);
                context.Articles.Add(a1);
                context.SaveChanges();*/

                //通过文章获取评论数据
                /*Article article = context.Articles.Include(a => a.comments).Single(a => a.Id == 1);
                Console.WriteLine(article.Id);
                Console.WriteLine(article.Title);
                foreach(Comment com in article.comments)
                {
                    Console.WriteLine(com.Id+","+com.Message);
                }*/

                //通过评论获取文章数据
                /* Comment comment = context.Comments.Include(c => c.TheArticle).Single(c => c.Id == 2);
                 Console.WriteLine(comment.Message);
                 Console.WriteLine(comment.TheArticle.Id+","+comment.TheArticle.Title);*/


                /*var value = context.Articles.Select(a => new { a.Id, a.Title }).First();*/

                /*var comment = context.Comments.Select(c => new { c.Id, c.TheArticleId }).Single(c => c.Id == 2);

                Console.WriteLine(comment.Id + "," + comment.TheArticleId);*/

            }
        }
    }

三,多对多

多对多的关系配置演示,一般使用老师学生的例子…

1.代码构成

在这里插入图片描述

  • 实体类
    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. 在多对多的关系配置中,由于每个实体中都会有不确定的对应关系,因此,这时候外键是无法进行描述的,只能采用第三方关联表进行描述关系,不过在EF Core中,我们不需要自己去维护和生成这个表,只需要我们配置好多对多的关系,EF Core会帮我们完成这一工作!
  2. 这两个类中都有彼此的List集合类

这样的实体类设置,是多对多关系的设置的一种处理…

  • 实体配置类
    class TeacherConfig : IEntityTypeConfiguration<Teacher>
    {
        public void Configure(EntityTypeBuilder<Teacher> builder)
        {
            builder.ToTable("T_Teachers");

            builder.Property(s => s.Name).IsUnicode().HasMaxLength(20);

        }
    }
    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. 现在来看配置类中代码,它的意义是:学生有很多老师,老师也有很多学生,并且设置关联表的名字
    之后的DbContext,Add-Migration Update-database等省略

2.测试例子

        static void Main(string[] args)
        {
            using(MyDbContext myDb =new MyDbContext())
            {

                /*Student s1 = new Student { Name="张三"};
                Student s2 = new Student { Name="李四"};
                Student s3 = new Student { Name="王五"};

                Teacher t1 = new Teacher { Name = "Tom" };
                Teacher t2 = new Teacher { Name = "Jerry" };
                Teacher t3 = new Teacher { Name = "Zack" };


                s1.Teachers.Add(t1);
                s1.Teachers.Add(t2);

                s2.Teachers.Add(t3);
                s2.Teachers.Add(t2);

                s3.Teachers.Add(t1);
                s3.Teachers.Add(t2);
                s3.Teachers.Add(t3);

                myDb.Teachers.Add(t1);
                myDb.Teachers.Add(t2);
                myDb.Teachers.Add(t3);

                myDb.Students.Add(s1);
                myDb.Students.Add(s2);
                myDb.Students.Add(s3);

                myDb.SaveChanges();*/


                //查询

                var teachers= myDb.Teachers.Include(t => t.Students);

                foreach(var s in teachers)
                {
                    Console.WriteLine(s.Name);
                    foreach(Student student in s.Students)
                    {
                        Console.WriteLine("\t"+student.Name);
                    }
                }
            }
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栀梦星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值