• asp.net core系列 26 EF模型配置(实体关系)


    一.概述

      EF实体关系定义了两个实体互相关联起来(主体实体和依赖实体的关系,对应数据库中主表和子表关系)。 在关系型数据库中,这种表示是通过外键约束来体现。本篇主要讲一对多的关系。先了解下描述关系的术语。

      (1) 依赖实体: 这是包含外键属性的实体(子表)。有时称为 child 。

      (2) 主体实体: 这是包含主/备用键属性的实体(主表)。 有时称为 parent。

      (3) 外键:依赖实体(子表)中的属性,用于存储主表的主键属性的值。

      (4) 主键: 唯一标识的主体实体(主表)的属性。 这可能是 primary key 或备用键。

      (5) 导航属性: 包含对相关实体引用,在的主体和或依赖实体上定义的属性。

        集合导航属性: 一个导航属性,对多个相关实体的引用。

        引用导航属性: 一个导航属性,对单个相关实体的引用。

             下面示例代码来说明Blog和Post之间的一对多关系。其中 Post 是依赖实体;Blog 是主体实体;Post.BlogId 是外键;Blog.BlogId 是主键;Post.Blog引用导航属性;Blog.Posts集合导航属性。

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }

    二.约定

      按照约定,当在实体类型上发现导航属性时,将创建关系。如果属性所指向的类型不能被当前数据库提供程序映射为标量类型,则将其视为导航属性。下面用三个示例来说明通过约定创建的主从实体关系。

      2.1 完全定义的关系

        关系最常见的模式是在关系的两端定义导航属性,并在依赖实体类中定义外键属性。

        (1)如果两个类型之间找到一对导航属性,则它们将被配置为同一关系的反转导航属性。

        (2)如果依赖实体包含名为的属性<primary key property name>, <navigation property name><primary key property name>,或<principal entity name><primary key property name>然后它将配置为外键。

        下面代码示例是完全定义的关系的实体,通过约定来创建主从实体关系。

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
        //反转导航属性
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        //通过约定(<primary key property name>)创建外键
        public int BlogId { get; set; }
        //反转导航属性
        public Blog Blog { get; set; }
    }

        使用EF基于数据模型(Blog和Post实体)创建数据库。生成后,查看数据表的关系映射,如下图所示:

        

      2.2  没有外键属性

        虽然建议在依赖实体类中定义外键属性,但这不是必需的。如果没有找到外键属性,那么将引入一个名为<navigation property name> > principal key property name>的隐藏外键属性(上篇有介绍)。在下面代码中,依赖实体Post中没有显示定义外键BlogId。

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public Blog Blog { get; set; }
    }

       2.3 单一导航属性

         仅包含一个导航属性(没有反转导航属性,也没有外键属性)就足以拥有约定定义的关系。还可以有一个导航属性和一个外键属性。代码如下所示产:

        public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    }

     三. 数据注释

       有两个数据注释可用于配置关系,[ForeignKey]和[InverseProperty]。

      3.1 ForeignKey可以将指定属性设置为外键属性。 这种设置通常是外键属性不被约定发现时,显示设置。如下面代码示例, 在依赖实体Post中将BlogForeignKey指定为外键。代码中生成的主从实体关系与上面的约定示例是一样的。

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogForeignKey { get; set; }
    
        [ForeignKey("BlogForeignKey")]
        public Blog Blog { get; set; }
    }

       下面使用EF基于数据模型(Blog和Post实体)创建数据库。生成后,查看数据表的关系映射,如下图所示:

      3.2 InverseProperty配置依赖实体和主体实体上的导航属性如何配对。当两个实体类型之间有一对以上的导航属性时,通常会这样做。如下面代码示例:

         public class Post
        {
            public int PostId { get; set; }
            public string Title { get; set; }
            public string Content { get; set; }
    
            public Blog Blog { get; set; }
    
            public User Author { get; set; }
    
            public User Contributor { get; set; }
        }
    
        public class User
        {
            public string UserId { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
    
            [InverseProperty("Author")]
            public List<Post> AuthoredPosts { get; set; }
    
            [InverseProperty("Contributor")]
            public List<Post> ContributedToPosts { get; set; }
        }

        下面使用EF基于数据模型(User和Post实体)创建数据库。生成后,查看数据表的关系映射,如下图所示:

    四. Fluent API

       要在Fluent API中配置关系,首先要确定组成关系的导航属性。HasOne或HasMany标识开始配置的实体类型上的导航属性。然后调用WithOneWithMany来标识反导航。HasOne/WithOne用于引用导航属性,而HasMany/WithMany用于集合导航属性。

      在EF基于现有数据库进行反向工程时,根据数据库将自动生成DbContext上下文类,里面重写了OnConfiguring方法。下面示例是一个MyContext上下文类,在OnModelCreating方法中确定了实体的关系。

      

      4.1 完全定义的关系

        class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog) //Post类有一个Blog引用导航属性
                .WithMany(b => b.Posts);//Blog类有一个Posts反导航集合
        }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
        //反导航集合
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        //引用导航属性
        public Blog Blog { get; set; }
    }

      4.2 单一导航属性

        如果只有一个导航属性,那么就会出现无参数重载的WithOne以及WithMany。这表明在关系的另一端有一个概念上的引用或集合,但是实体类中不包含导航属性。

    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Blog>()
                .HasMany(b => b.Posts)//blog类有一个集合导航属性
                .WithOne();
        }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    }

        下面使用EF基于数据模型(User和Post实体)创建数据库。生成后,查看数据表的关系映射,如下图所示:

      4.3 外键

        可以使用 Fluent API 配置哪些属性应用作给定关系外键属性。

        class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog)
                .WithMany(b => b.Posts)
                .HasForeignKey(p => p.BlogForeignKey);
        }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogForeignKey { get; set; }
        public Blog Blog { get; set; }
    }

        以下代码列表演示如何配置复合外键。

    class MyContext : DbContext
    {
        public DbSet<Car> Cars { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Car>()
                .HasKey(c => new { c.State, c.LicensePlate });
    
            modelBuilder.Entity<RecordOfSale>()
                .HasOne(s => s.Car)
                .WithMany(c => c.SaleHistory)
                .HasForeignKey(s => new { s.CarState, s.CarLicensePlate });
        }
    }
    
    public class Car
    {
        public string State { get; set; }
        public string LicensePlate { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
    
        public List<RecordOfSale> SaleHistory { get; set; }
    }
    
    public class RecordOfSale
    {
        public int RecordOfSaleId { get; set; }
        public DateTime DateSold { get; set; }
        public decimal Price { get; set; }
    
        public string CarState { get; set; }
        public string CarLicensePlate { get; set; }
        public Car Car { get; set; }
    }

      总结:关于实体关系,还讲到了“必需和可选的关系”、“级联删除”。以及关系模式中的“一对一关系”、“多对多关系",这些以后用到再参考文档。 个人认为在传统开发中,以建库建表优先的情况下,不会去设置数据表的外键关系,这种关系是由编程去控制。 这样对数据库进行反向工程时,也不会生成有关系的主从实体模型。

     参考文献:

        官方文档:EF 实体关系

  • 相关阅读:
    [已读]精通AngularJS
    [在读]HTML5数据推送应用开发
    [未读]深入浅出node.js
    [已读]用Angularjs开发下一代web应用
    [在读]Secrets of the javascript Ninja
    Element ui select同时获取value和label的值
    Element ui tree树形控件获取父节点id
    Element ui tree结合Vue使用遇到的一些问题(一)
    vue数组检测更新问题
    css3实现流星坠落效果
  • 原文地址:https://www.cnblogs.com/MrHSR/p/10393861.html
Copyright © 2020-2023  润新知