一.概述

  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 实体关系

asp.net core系列 26 EF模型配置(实体关系)的更多相关文章

  1. asp.net core系列 28 EF模型配置(字段,构造函数,拥有实体类型)

    一. 支持字段 EF允许读取或写入字段而不是一个属性.在使用实体类时,用面向对象的封装来限制或增强应用程序代码对数据访问的语义时,这可能很有用.无法使用数据注释配置.除了约定,还可以使用Fluent ...

  2. asp.net core系列 23 EF模型配置(概述, 类型和属性的包含与排除)

    一.模型配置概述 EF使用一组约定基于实体类的定义来构建模型. 可指定其他配置以补充或替代约定的内容.本系列介绍的配置可应用于面向任何数据存储的模型,以及面向任意关系数据库时可应用的配置. 数据库提供 ...

  3. asp.net core系列 27 EF模型配置(索引,备用键,继承)

    一.索引 索引是许多数据存储中的常见概念.虽然它们在数据存储中的实现可能会有所不同,但它们可用于更有效地基于列(或列集)进行查找.按照约定,用作外键每个属性 (或组的属性) 会自动创建索引.无法使用数 ...

  4. asp.net core系列 25 EF模型配置(隐藏属性)

    一. 隐藏属性概述 隐藏属性也叫影子属性,该属性不是在.net实体类中定义的属性,而是在EFCore模型中为该实体类型定义的属性.这些属性的值和状态完全在变更跟踪器中维护.它有二个功能:(1)当数据库 ...

  5. asp.net core系列 29 EF模型配置(查询类型,关系数据库建模)

    一.查询类型 此功能是EF Core 2.1中的新功能. EF Core除了实体类型之外,EF Core模型还可以包含查询类型,这些查询类型是针对“未映射到实体类型”的数据获取.比如视图,或只读数据表 ...

  6. asp.net core系列 24 EF模型配置(主键,生成值,最大长度,并发标记)

    一.主键 键用作每个实体实例的主要唯一标识符. 使用关系数据库时,这会映射到主键的概念. 还可以配置不是主键的唯一标识符.按照约定,名为 Id 或 <type name>Id 的属性会配置 ...

  7. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  8. asp.net core系列 31 EF管理数据库架构--必备知识 反向工程

    一.   反向工程 反向工程是基于数据库架构,生成的实体类和DbContext类代码的过程,对于Visual Studio开发,建议使用PMC.对于其他开发环境,请选择.NET Core CLI工具( ...

  9. asp.net core 系列 22 EF(连接字符串,连接复原,DbContext)

    一.连接字符串 在上二篇中,ASP.NET Core 应用程序连接字符串是写死在ConfigureServices代码中,下面介绍通过配置来实现.连接字符串可以存储在 appsettings.json ...

随机推荐

  1. linux查看空间情况----df与du命令

    1.查看整个磁盘空间使用情况,可以使用命令 :df   -h 2.获取当前的文件夹下的磁盘使用情况,可以使用如下命令:du --max-depth=1 -h 使用 du --max-depth=1 - ...

  2. 转载 CSDN 谈谈我对证券公司一些部门的理解(前、中、后台)

    谈谈我对证券公司一些部门的理解(前.中.后台) 2018年02月08日 15:11:07 unirong 阅读数:2165   文中对各大部门的分析都是从作者多年经历总结出来的有感之谈,尤其是前台的6 ...

  3. kali安装vm tools

    kali安装完毕后第一件事便是安装VMtools,tools能让kali和物理机进行交互,复制粘贴功能等. 在屏幕上会弹出一个光驱,点击进去,进入文件夹 将文件复制到home文件夹下. 1 tar z ...

  4. [CF1093E]Intersection of Permutations

    [CF1093E]Intersection of Permutations 题目大意: 给定两个长度为\(n(n\le2\times10^5)\)的排列\(A,B\).\(m(m\le2\times1 ...

  5. HTML入门13

    构建表格 使用colspan和rowspan添加无单位的数字值作为属性来实现行合并和列合并: <col>来定义列的样式,每一个<col>都会制定每列的样式,对于不需要指定列的样 ...

  6. spring-cloud-Zuul学习(二)【基础篇】--典型配置【重新定义spring cloud实践】

    -- 2019-04-15 20:22:34 引言 上一节是一个最基本的zuul网关实例,它是整个spring-cloud生态里面“路由-服务”的一个缩影,后续也就是锦上添花.这节主要讲述zuul的一 ...

  7. Jmeter之Non HTTP response code: java.net.SocketException/Non HTTP response message: Permission denied: connect

    最近在做性能测试过程中遇到了高并发时,后台监控各项指标都很正常,但是测试结果中很多Non HTTP response code: java.net.SocketException/Non HTTP r ...

  8. ORACLE 查询近一天,近半小时内的数据

    SELECT 字段  FROM 表名  WHERE 时间字段  BETWEEN SYSDATE-1 AND SYSDATE; //查询一天内的数据 sysdate+1 加一天sysdate+1/24 ...

  9. java小练习

    打印99乘法表 因为有9行9列,所有要用两个for循环 int m; for (int i = 1; i < 10; i++) { for (int j = 1; j <= i; j++) ...

  10. 企业IT管理员IE11升级指南【14】—— IE11代理服务器配置

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...