Net6 EFCore CodeFirst FluentApi关系配置

十年河东十年河西,莫欺少年穷

学无止境,精益求精  

微软官网:https://learn.microsoft.com/zh-cn/ef/ef6/modeling/code-first/fluent/relationships

NetCore3.1 请参考;https://www.cnblogs.com/zzy-tongzhi-cnblog/p/14366894.html

codefirst 中如果要建立两张表的主外键关系,该如何操作呢?

在EfCore 中实体关系的配置主要有3中模式

一对一 :HasOne(....).WithOne(....)

一对多 :HasOne(....).WithMany(....)

多对多 :HasMany(....).WithMany(....)

1、一对多关系【双向导航属性】

项目中新建两张表,一张为新闻表,一张为评论表,一篇新闻对应多个评论

    //文章
    public class Article
    {
        public long id { get; set; }
        public string title { get; set; } 
        public string info { get; set; }
        public DateTime pubdate { get; set; }
        public List<Comment> comments { get; set; } = new List<Comment>();
    }
    //评论
    public class Comment
    {
        public long id { get; set; }
        public string uname { get; set; }
        public string message { get; set; } 
        public Article articleId { get; set; }
    }

1.1、配置一对多关系

(方式1)

    public class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles");
            builder.HasMany<Comment>(A => A.comments).WithOne(A => A.articleId);
        }
    }

    public class CommentConfig : IEntityTypeConfiguration<Comment>
    {
        public void Configure(EntityTypeBuilder<Comment> builder)
        {
            builder.ToTable("T_Comments");
            builder.Property("uname").HasMaxLength(50).IsRequired(true).HasComment("评论者姓名");
        }
    }

    public class wechatDbContext : DbContext
    {
        public DbSet<Article> Articles { get; set; }
        public DbSet<Comment> Comments { get; set; } 

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=demo1;Integrated Security=True");

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //从当前程序集命名空间加载所有的IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
View Code
builder.HasMany<Comment>(A => A.comments).WithOne(A => A.articleId);

(方式2) 

 builder.HasOne<Article>(A => A.articleId).WithMany(A => A.comments);

1.2、显式指定外键列

    //文章 一对多
    public class Article
    {
        public long id { get; set; }
        public string title { get; set; } 
        public string info { get; set; }
        public DateTime pubdate { get; set; }
        public List<Comment> Comments { get; set; } = new List<Comment>();
    }
    //评论  一对多
    public class Comment
    {
        public long id { get; set; }
        public string uname { get; set; }
        public string message { get; set; }
        public long articleId { get; set; }
        public Article article { get; set; } 
    }

配置关系并指定外键列

    public class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles");
            //显式指定外键列
            builder.HasMany<Comment>(A => A.Comments).WithOne(A => A.article).HasForeignKey(A => A.articleId);
        }
    }

建议使用这种显式指定外键列的方式进行

2、配置一对一关系

假设一篇文章只允许一条评论

实体模型对应关系如下【一对一关系需要显示指定外键】:

    //文章 一对一
    public class Article
    {
        public long id { get; set; }
        public string title { get; set; } 
        public string info { get; set; }
        public DateTime pubdate { get; set; }
        public Comment comment { get; set; }
    }
    //评论 一对一
    public class Comment
    {
        public long id { get; set; }
        public string uname { get; set; }
        public string message { get; set; }
        public long articleId { get; set; }
        public Article article { get; set; }
        public long acticleId { get; set; }
    }

(方式一)

    public class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles"); 
        }
    }

    public class CommentConfig : IEntityTypeConfiguration<Comment>
    {
        public void Configure(EntityTypeBuilder<Comment> builder)
        {
            builder.ToTable("T_Comments");
            builder.Property("uname").HasMaxLength(50).IsRequired(true).HasComment("评论者姓名");

            builder.HasOne<Article>(A => A.article).WithOne(d => d.comment).HasForeignKey<Comment>(A => A.acticleId);

        }
    }

    public class wechatDbContext : DbContext
    {
        public DbSet<Article> Articles { get; set; }
        public DbSet<Comment> Comments { get; set; } 

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=demo2;Integrated Security=True");

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //从当前程序集命名空间加载所有的IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
View Code
builder.HasOne<Article>(A => A.article).WithOne(d => d.comment).HasForeignKey<Comment>(A => A.acticleId); 

(方式二)

  builder.HasOne<Comment>(A => A.comment).WithOne(d => d.article).HasForeignKey<Comment>(A => A.acticleId);

 

 

 3、配置多对多关系

一个老师有多个学生,一个学生有多个老师

    //老师 多对多
    public class Teacher
    {
        public long id { get; set; }
        public string tname { get; set; }    
        public List<Student> students { get; set; }
    }
    //学生 多对多
    public class Student
    {
        public long id { get; set; }
        public string sname { get; set; }
        public List<Teacher>  teachers { get; set; }
    }

(方式一)

    public class ArticleConfig : IEntityTypeConfiguration<Teacher>
    {
        public void Configure(EntityTypeBuilder<Teacher> builder)
        {
            builder.ToTable("T_Teachers");
            //T_Students_Teachers 为中间表  在数据库中 多对多必须存在中间表
            builder.HasMany<Student>(A => A.students).WithMany(d => d.teachers).UsingEntity(A => A.ToTable("T_Students_Teachers"));

        }
    }

    public class CommentConfig : IEntityTypeConfiguration<Student>
    {
        public void Configure(EntityTypeBuilder<Student> builder)
        {
            builder.ToTable("T_Studentss");

          //  builder.HasMany<Teacher>(A => A.teachers).WithMany(d => d.students).UsingEntity(A => A.ToTable("T_Students_Teachers"));
        }
    }

    public class wechatDbContext : DbContext
    {
        public DbSet<Teacher> teachers { get; set; }
        public DbSet<Student>  students { get; set; } 

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=demo4;Integrated Security=True");

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //从当前程序集命名空间加载所有的IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
View Code
//T_Students_Teachers 为中间表名称  在数据库中 多对多必须存在中间表
  builder.HasMany<Student>(A => A.students).WithMany(d => d.teachers).UsingEntity(A => A.ToTable("T_Students_Teachers"));

(方式二)

    public class CommentConfig : IEntityTypeConfiguration<Student>
    {
        public void Configure(EntityTypeBuilder<Student> builder)
        {
            builder.ToTable("T_Studentss");

           builder.HasMany<Teacher>(A => A.teachers).WithMany(d => d.students).UsingEntity(A => A.ToTable("T_Students_Teachers"));
        }
    }

 4、一对多关系配置【单向导航】

使用场景,

假设我们有用户表、请假表、离职表、入职表、婚假表等,其他表都需要使用到用户表,如果此时我们使用双向导航属性,则会使用户表过于复杂,因此,此时需要单向导航属性

    //文章 一对多[单向导航]
    public class Article
    {
        public long id { get; set; }
        public string title { get; set; } 
        public string info { get; set; }
        public DateTime pubdate { get; set; }
    }
    //评论  一对多[单向导航]
    public class Comment
    {
        public long id { get; set; }
        public string uname { get; set; }
        public string message { get; set; }
        public long articleId { get; set; }
        public Article article { get; set; } 
    }

此时,Article 无需引用评论表

    public class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.ToTable("T_Articles");
        }
    }

    public class CommentConfig : IEntityTypeConfiguration<Comment>
    {
        public void Configure(EntityTypeBuilder<Comment> builder)
        {
            builder.ToTable("T_Comments");

            //显式指定外键列
            builder.HasOne<Article>(A => A.article).WithMany().HasForeignKey(A => A.articleId);
        }
    }

    public class wechatDbContext : DbContext
    {
        public DbSet<Article> Articles { get; set; }
        public DbSet<Comment> Comments { get; set; } 

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Data Source=LAPTOP-84R6S0FB;Initial Catalog=demo1;Integrated Security=True");

        }

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

  //单向导航属性、并显式指定外键列
  builder.HasOne<Article>(A => A.article).WithMany().HasForeignKey(A => A.articleId);

 单向导航属性用于针对公共表和其他表之前,因为公共表会被经常使用,因此尽量避免使用双向导航

5、特殊结构【树状结构】

例如公司的组织架构、权限表等

 

 实体类如下:

    //树状结构
    public class OrgUint
    {
        public Guid OrgId { get; set; }
        public string OrgName { get; set; }
        public OrgUint Parent { get; set; }
        public List<OrgUint> Childs { get; set; } = new List<OrgUint>();
    }

关系配置

    public class OrgUintConfig : IEntityTypeConfiguration<OrgUint>
    {
        public void Configure(EntityTypeBuilder<OrgUint> builder)
        {
            builder.ToTable("T_OrgUints");

            builder.Property(A => A.OrgId).HasMaxLength(50);
            builder.HasKey(A => A.OrgId);
            builder.HasOne<OrgUint>(A => A.Parent).WithMany(A => A.Childs);//根节点没有父节点,因此不能设置为IsRequired
        }
    }

 

 数据测试:

        static async Task Main(string[] args)
        {
            using (wechatDbContext context=new wechatDbContext())
            {
                OrgUint org_1 = new OrgUint()
                {
                    OrgName = "全球",
                    OrgId=Guid.NewGuid()
                }; 
                OrgUint org_2 = new OrgUint()
                {
                    OrgName = "亚洲",
                    OrgId = Guid.NewGuid()
                };
                org_2.Parent = org_1;
                OrgUint org_3 = new OrgUint()
                {
                    OrgName = "大洋洲",
                    OrgId = Guid.NewGuid()
                };
                org_3.Parent = org_1;
                OrgUint org_4 = new OrgUint()
                {
                    OrgName = "欧洲",
                    OrgId = Guid.NewGuid()
                };
                org_4.Parent = org_1;
                OrgUint org_5 = new OrgUint()
                {
                    OrgName = "南极洲",
                    OrgId = Guid.NewGuid()
                };

                org_5.Parent = org_1;
                OrgUint org_6 = new OrgUint()
                {
                    OrgName = "中国",
                    OrgId = Guid.NewGuid()
                };
                org_6.Parent = org_2;
                OrgUint org_7 = new OrgUint()
                {
                    OrgName = "日本",
                    OrgId = Guid.NewGuid()
                };
                org_7.Parent = org_2;
                OrgUint org_8 = new OrgUint()
                {
                    OrgName = "英国",
                    OrgId = Guid.NewGuid()
                };
                org_8.Parent = org_4;
                //
                context.OrgUints.AddRange(new List<OrgUint>() { org_1, org_2, org_3, org_4, org_5, org_6, org_7, org_8 });
                context.SaveChanges();

            } 
            Console.Read();
        } 
View Code

数据递归查询

        static void
             Main(string[] args)
        {
            using (wechatDbContext context=new wechatDbContext())
            {
                var org = context.OrgUints.Where(A => A.Parent == null).FirstOrDefault(); 
                Console.WriteLine(new String('.', 1) + org.OrgName);
                Print(10, context, org);
            } 
            Console.Read();
        } 

        static void Print(int Printlevel, wechatDbContext  context,OrgUint org)
        {
            var childs = context.OrgUints.Where(A => A.Parent == org).ToList();
            foreach(var item in childs)
            {
                Console.WriteLine(new String('.', Printlevel) + item.OrgName);
                Print(Printlevel*2, context, item);
            }
        }

 以上就是CodeFitst 配置一对一 、一对多、多对多的方式

FluentApi总结

1.FluentApi简介

  EF中的FluentApi作用是通过配置领域类来覆盖默认的约定。在EF中,我们通过DbModelBuilder类来使用FluentApi,它的功能比数据注释属性更强大。

使用FluentApi时,我们在context类的OnModelCreating()方法中重写配置项,一个栗子:

复制代码
public class SchoolContext: DbContext 
{

    public DbSet<Student> Students { get; set; }
        
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Write Fluent API configurations here

    }
}
复制代码

  我们可以把FluentApi和数据注释属性一起使用,当FluentApi和数据注释属性都配置了同一个项时,采用FluentApi中的配置。

在EF6中FluentApi可以配置领域类的以下几个方面,下表也列出了一些常用的FluentApi方法及其作用:

配置Fluent API 方法作用
架构相关配置 HasDefaultSchema() 数据库的默认架构
ComplexType() 把一个类配置为复杂类型
实体相关配置 HasIndex() 实体的的索引
HasKey() 实体的主键(可其实现复合主键,[Key]在EF core中不能实现复合主键)
HasMany() 1对多的或者 多对多关系 
HasOptional() 一个可选的关系,这样配置会在数据库中生成一个可空的外键
HasRequired() 一个必有的关系,这样配置会在数据库中生成一个不能为空的外键
Ignore() 实体或者实体的属性不映射到数据库
Map() 设置一些优先的配置
MapToStoredProcedures() 实体的CUD操作使用存储过程
ToTable() 为实体设置表名
属性相关配置 HasColumnAnnotation() 给属性设置注释
IsRequired() 在调用SaveChanges()方法时,属性不能为空
IsOptional() 可选的,在数据库生成可空的列
HasParameterName() 配置用于该属性的存储过程的参数名
HasDatabaseGeneratedOption() 配置数据库中对应列的值怎样生成的,如计算,自增等
HasColumnOrder() 配置数据库中对应列的排列顺序
HasColumnType() 配置数据库中对应列的数据类型
HasColumnName() 配置数据库中对应列的列名
IsConcurrencyToken() 配置数据库中对应列用于乐观并发检测

 

 

@天才卧龙的博科人

posted @ 2022-09-23 11:08  天才卧龙  阅读(478)  评论(0)    收藏  举报