zoukankan      html  css  js  c++  java
  • EntityFramework Core实现一对一、一对多、多对多关系

    参考资料:
    杨旭教程:https://www.bilibili.com/video/BV1xa4y1v7rR?p=4

    准备工作

    根据教程前几节,已经建立好了三个实体类,并且生成了数据库。三个实体类分别是:

    联赛League:

    public class League
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string Name { get; set; }
    
        [Required, MaxLength(50)]
        public string Country { get; set; }
    }
    

    球员Player:

    public class Player
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }
    

    俱乐部Club:

    public class Club
    {
        public Club()
        {
            Players = new List<Player>();
        }
    
        public int Id { get; set; }
        public string Name { get; set; }
        public string City { get; set; }
    
        [Column(TypeName = "date")]
        public DateTime DateOfEstablished { get; set; }
        public string History { get; set; }
    
        public League League { get; set; } // 导航属性
        public List<Player> Players { get; set; } // 导航属性
    }
    

    一对多的关系

    Club有一个导航属性League,导航到单个League上,一个Club都对应一个League,但可能有多个Club对应到同一个League上,所以Club对League就是多对一的关系。所以Club表中应该存在一个外键对应League,在代码中看不出,可以在数据库中看一下:

    UTOOLS1593075425872.png

    确实存在。这种关系可以在代码中指定,不指定也会自动生成。

    Club的Players属性也是另一种形式的导航属性,这个导航属性的类型是一个集合,相当于另一个方向的导航。这个时候Club是主表,Player是子表。Players表中也有一个ClubId外键。

    多对多的关系

    计划再创建一个比赛Game实体模型,它与球员Player之间是m:n的关系。这种关系使用EF Core无法直接实现,可以加一个中间表GamePlayer。比如一个队员,这个赛季参加了5场比赛,它就应该对应5哥GamePlayer,一对多的关系。而每场比赛,又有多个队员参加,每个队员又相当于这场比赛的一个GamePlayer,所以Game和GamePlayer也是一对多的关系。这样Player和Game之间就相当于间接形成了多对多的关系。

    比赛Game:

    public class Game
    {
        public int Id { get; set; }
        public int Round { get; set; }
        public DateTimeOffset? StartTime { get; set; }
    }
    

    因为StartTime是具体的时间,所以用DateTimeOffset类型。

    再建一个GamePlayer类:

    
    

    该类是Games和Players的中间表,只需要包含它们两个的主键就行了,作为GamePlayer的外键。而GamePlayer不再需要多余的主键了,可以用两个外键做联合主键。

    先去Player类中添加一个导航属性,因为是List类型,所以初始化一下,以免出现空指针空引用异常NullReferenceExcepiton

    public class Player
    {
        public Player()
        {
            GamePlayers = new List<GamePlayer>();
        }
    
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    
        public List<GamePlayer> GamePlayers { get; set; }
    }
    

    Game类中也是一样:

    public class Game
    {
        public Game()
        {
            GamePlayers = new List<GamePlayer>();
        }
    
        public int Id { get; set; }
        public int Round { get; set; }
        public DateTimeOffset? StartTime { get; set; }
    
        public List<GamePlayer> GamePlayers { get; set; }
    }
    

    现在在Game和Player类中都体现了一对多的关系,可以回GamePlayer类中再体现一下一对多的关系:

    public class GamePlayer
    {
        public int GameId { get; set; }
        public int PlayerId { get; set; }
    
        public Game Game { get; set; }
        public Player Player { get; set; }
    }
    

    这种一对多的关系可以两端同时体现。然后必须手动设定联合主键。可以去数据库上下文DbContext中,重写一下OnModelCreating方法,在里面使用Fluent API来设定:

    public class DemoDbContext: DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=YangDemo;Integrated Security=True");
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<GamePlayer>().HasKey(x => new
            {
                x.PlayerId,
                x.GameId
            });
        }
    
        public DbSet<League> Leagues { get; set; }
        public DbSet<Club> Clubs { get; set; }
        public DbSet<Player> Players { get; set; }
        public DbSet<Game> Games { get; set; }
        public DbSet<GamePlayer> GamePlayers { get; set; }
    }
    

    使用modelBuilder,选择GamePlayer实体,使用HasKey方法来设定主键。联合主键就是后面的匿名类中的两个属性。然后添加迁移并更新数据库。

    可以看到GamePlayers表中的联合主键:

    UTOOLS1593077807952.png

    一对一的关系

    假设每个队员Player对应一份简历Resume,一份简历也只属于一个队员。

    建立简历Resume类:

    public class Resume
    {
        public int Id { get; set; }
        public string Description { get; set; }
    
        public int PlayerId { get; set; }
        public Player Player { get; set; }
    }
    

    除了简历自己的属性之外,需要建立一个与Player类的主键相同类型的属性,在这里是int类型,并命名为PlayerId,然后建立到Player类的导航属性。

    在Player表中做同样操作:

    public int ResumeId { get; set; }
    public Resume Resume { get; set; }
    

    现在Player和Resume就是一对一的关系,EF Core会选择其中的一个类作为主体,但是EF Core很可能选错,所以还是要使用Fluent API来手动指定一下

    回到数据库上下文中。这次Entity选择哪一项都可以。但我们主体应该是Player,也就是Resume应该有一个外键是Player的Id。

    modelBuilder.Entity<Resume>()
                    .HasOne(x => x.Player)
                    .WithOne(x => x.Resume)
                    .HasForeignKey<Resume>(x => x.PlayerId);
    

    大意是Resume实体有一个Player,一个Player也有一个Resume,Resume有外键PlayerId。然后就可以添加迁移并更新数据库了。

  • 相关阅读:
    lombok 异常:Lombok needs a default constructor in the base class less... (Ctrl+F1) Inspe
    kinaba 安装踩坑: FATAL Error: [elasticsearch.url]: definition for this key is missing
    使用Java将搜狗词库文件(文件后缀为.scel)转为.txt文件
    RedHat linux服务器安装elasticsearch且设置公网访问
    return array 评论添加状态和提示信息
    .保护Express应用程序
    SQL Injection(SQL注入漏洞)
    POST在发送数据的时候使用的是HTTP命令
    assert_option()可以用来对assert()进行一些约束和控制
    supercool.sh文件里,有哪些恶意的命令
  • 原文地址:https://www.cnblogs.com/Kit-L/p/13192599.html
Copyright © 2011-2022 走看看