1.关系

关系定义两个实体之间的关系。在关系型数据库中,这由外键约束表示。

2.术语定义

有许多术语用于描述关系:
●相关实体:这是包含外键属性的实体。有时称为关系的"子级"。
●主体实体:这是包含主/备用键属性的实体。有时称为关系的 "父项"。
●外键:依赖实体中的属性,用于存储与实体相关的主体键属性的值。
●主体密钥:唯一标识主体实体的属性。这可能是主键或备用密钥。
●导航属性:在主体和/或从属实体上定义的属性,该属性包含对相关实体的引用。
●集合导航属性:一个导航属性,其中包含对多个相关实体的引用。
●引用导航属性:保存对单个相关实体的引用的导航属性。
●反向导航属性:讨论特定导航属性时,此术语是指关系另一端的导航属性。
下面的代码列表显示了与之间Blog的一对多关系Post
●Post是依赖实体
●Blog是主体实体
●Post.BlogId为外键
●Blog.BlogId是主体键(在这种情况下是主键,而不是备用键)
●Post.Blog是一个引用导航属性
●Blog.Posts是集合导航属性
●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; }
}

3.约定

按照约定,当发现类型上有导航属性时,将创建关系。如果属性指向的类型不能由当前的数据库提供程序映射为标量类型,则该属性视为一个导航属性。

4.完全定义的关系

关系最常见的模式是在关系两端定义导航属性,在依赖实体类中定义外键属性。
如果在两个类型之间找到一对导航属性,则这些属性将配置为同一关系的反向导航属性。
如果依赖实体包含名为<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; }
//外键属性
public int BlogId { get; set; }
//反向导航属性
public Blog Blog { get; set; }
}

5.无外键属性

尽管建议在依赖实体类中定义外键属性,但这并不是必需的。如果未找到外键属性,则会以该名称<navigation property name><principal 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; }
//阴影反向导航属性
public Blog Blog { get; set; }
}

6.单个导航属性

只包含一个导航属性(无反向导航,没有外键属性)就足以具有约定定义的关系。 还可以有一个导航属性和一个外键属性。

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; }
}

7.数据注释

可以使用两个数据批注来配置关系[ForeignKey]和[InverseProperty]。System.ComponentModel.DataAnnotations.Schema命名空间中提供了这些项。

7.1ForeignKey

你可以使用数据批注来配置应用程序作给定关系的外键属性的属性。通常,当不按约定发现外键属性时,会执行此操作。

namespace EFModeling.DataAnnotations.Relationships.ForeignKey
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
#region Entities
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; }
}
#endregion
}

7.2InverseProperty

您可以使用数据批注来配置依赖项和主体实体上的导航属性如何配对。这通常在两个实体类型之间存在多个导航属性对时执行。

namespace EFModeling.DataAnnotations.Relationships.InverseProperty
{
class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
}
#region Entities
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; } public int AuthorUserId { get; set; }
public User Author { get; set; } public int ContributorUserId { 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; }
}
#endregion
}

8.Fluent API

若要在熟知的API中配置关系,请首先标识构成关系的导航属性。HasOne或HasMany标识要开始配置的实体类型上的导航属性。然后,将调用链接到WithOne或WithMany以标识反向导航。HasOne/WithOne用于引用导航属性,HasMany / WithMany用于集合导航属性。

namespace EFModeling.FluentAPI.Relationships.NoForeignKey
{
#region Model
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);
}
}
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; }
}
#endregion
}

8.1单个导航属性

如果只有一个导航属性,则用WithOne、WithMany的无参数重载。这表示在概念上,关系的另一端有一个引用或集合,但实体类中不包含导航属性。

namespace EFModeling.FluentAPI.Relationships.OneNavigation
{
#region Model
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)
.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; }
}
#endregion
}

8.2ForeignKey

你可以使用API来配置应用程序的外键属性。

namespace EFModeling.Configuring.DataAnnotations.Samples.Relationships.ForeignKey
{
#region Model
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; }
}
#endregion
}

下面的代码列表演示如何配置复合外键:

namespace EFModeling.Configuring.DataAnnotations.Samples.Relationships.CompositeForeignKey
{
#region Model
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; }
//State对应CarState
public string CarState { get; set; }
//LicensePlate 对应CarLicensePlate
public string CarLicensePlate { get; set; }
public Car Car { get; set; }
}
#endregion
}

您可以使用的HasForeignKey(...)字符串重载将影子属性配置为外键。建议先将影子属性显式添加到模型,然后再将其用作外键:

class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Add the shadow property to the model
modelBuilder.Entity<Post>()
//配置外键
.Property<int>("BlogForeignKey");
// Use the shadow property as a foreign key
modelBuilder.Entity<Post>()
//配置一对多关系
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
//配置外键
.HasForeignKey("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 Blog Blog { get; set; }
}

8.3无导航属性

不一定需要提供导航属性。你可以直接在关系的一端提供外键。

namespace EFModeling.FluentAPI.Relationships.NoNavigation
{
#region Model
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<Blog>()
.WithMany()
//配置外键
.HasForeignKey(p => p.BlogId);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { 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; }
}
#endregion
}

9.主体密钥

如果你希望外键引用主键之外的属性,则可以使用熟知的API来配置关系的主体键属性。 配置为主体密钥的属性将自动设置为备用密钥。

class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RecordOfSale>()
.HasOne(s => s.Car)
.WithMany(c => c.SaleHistory)
.HasForeignKey(s => s.CarLicensePlate)
.HasPrincipalKey(c => c.LicensePlate);
}
}
public class Car
{
public int CarId { 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 CarLicensePlate { get; set; }
public Car Car { get; set; }
}

下面的代码列表演示如何配置复合主体键:

class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RecordOfSale>()
.HasOne(s => s.Car)
.WithMany(c => c.SaleHistory)
.HasForeignKey(s => new { s.CarState, s.CarLicensePlate })
.HasPrincipalKey(c => new { c.State, c.LicensePlate });
}
}
public class Car
{
public int CarId { get; set; }
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; }
}

10.必需和可选的关系

您可以使用熟知的API来配置是必需的还是可选的关系。最终,这会控制外键属性是必需的还是可选的。当使用阴影状态外键时,这非常有用。如果实体类中具有外键属性,则关系的requiredness取决于外键属性是必需还是可选。

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)
.IsRequired();
}
}
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; }
}

11.级联删除

您可以使用熟知的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)
.OnDelete(DeleteBehavior.Cascade);
}
}
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; }
}

12.其他关系模式

12.1一对一

一对多关系在两侧都有一个引用导航属性。它们遵循与一对多关系相同的约定,但在外键属性上引入了唯一索引,以确保只有一个依赖项与每个主体相关。

12.1.1数据注释
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public BlogImage BlogImage { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; } public int BlogId { get; set; }
public Blog Blog { get; set; }
}
12.1.2Fluent API

使用API 配置关系时,请使用HasOne和WithOne方法。配置外键时,需要指定依赖实体类型,请注意以下列表HasForeignKey中提供的泛型参数。在一对多关系中,可以清楚地表明具有引用导航的实体是依赖项,并且具有集合的实体是主体。但这并不是一对一的关系,因此需要显式定义它。

class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(p => p.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public BlogImage BlogImage { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; } public int BlogForeignKey { get; set; }
public Blog Blog { get; set; }
}

12.2多对多

目前尚不支持多对多关系,没有实体类来表示联接表。但是,您可以通过包含联接表的实体类并映射两个不同的一对多关系,来表示多对多关系。

class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(pt => new { pt.PostId, pt.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; } public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; } public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; } public string TagId { get; set; }
public Tag Tag { get; set; }
}

参考文献:
关系

(21)ASP.NET Core EF创建模型(关系)的更多相关文章

  1. (22)ASP.NET Core EF创建模型(索引、备用键、继承、支持字段)

    1.索引 索引是跨多个数据存储区的常见概念.尽管它们在数据存储中的实现可能会有所不同,但也可用于基于列(或一组列)更高效地进行查找. 1.1约定 按照约定,将在用作外键的每个属性(或一组属性)中创建索 ...

  2. (20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)

    1.必需和可选属性 如果实体属性可以包含null,则将其视为可选.如果属性的有效值不可以包含null,则将其视为必需属性.映射到关系数据库架构时,必需的属性将创建为不可为null的列,而可选属性则创建 ...

  3. (19)ASP.NET Core EF创建模型(包含属性和排除属性、主键、生成的值)

    1.什么是Fluent API? EF中内嵌的约定将POCO类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体映射到约定指示外的其他对象,所以Fluent API和注解都是一种方法,这两种方法 ...

  4. C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制

    在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Cachi ...

  5. asp.net core+ef core

    asp.net core+ef core 官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一 ...

  6. 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】

    Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...

  7. 使用 ASP.NET Core MVC 创建 Web API(二)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 六.添加数据库上下文 数据库上下文是使用Entity Framewor ...

  8. 使用 ASP.NET Core MVC 创建 Web API(四)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

  9. 使用 ASP.NET Core MVC 创建 Web API——响应数据的内容协商(七)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

随机推荐

  1. Python Flask高级编程之RESTFul API前后端分离精讲 (网盘免费分享)

    Python Flask高级编程之RESTFul API前后端分离精讲 (免费分享)  点击链接或搜索QQ号直接加群获取其它资料: 链接:https://pan.baidu.com/s/12eKrJK ...

  2. charles 黑名单

    本文参考:charles 黑名单 charles 黑名单 功能:阻止对匹配HOST的请求:可以直接把请求丢掉,也可以直接返回403状态码: 我一般用黑名单工具来block一些软件的自动上传功能 黑名单 ...

  3. Qt信号槽-原理分析

    目录 一.问题 二.Moc 1.变量 2.Q_OBJECT展开后的函数声明 3.自定义信号 三.connect 四.信号触发 1.直连 2.队列连接 五.总结 六.推荐阅读 一.问题 学习Qt有一段时 ...

  4. Kafka系列一之架构介绍和安装

    Kafka架构介绍和安装 写在前面 还是那句话,当你学习一个新的东西之前,你总得知道这个东西是什么?这个东西可以用来做什么?然后你才会去学习它,使用它.简单来说,kafka既是一个消息队列,如今,它也 ...

  5. 阿里云搭建nginx + uWSGI 实现 django 项目

    系统版本 CentOS/7 64位 1.安装使用python3 创建python3目录 sudo mkdir /usr/local/python3 进入python3目录 cd /usr/local/ ...

  6. DocumentFormat.OpenXml read excel file

    这里向大家介绍一种读取excel 数据的方法,用的是DoucmentFormat.OpenXml.dll 废话不多说,向大家展示一下在项目中处理过的方法,如果有任何疑问,随时联系我. using Do ...

  7. java进阶文章优选链接,面试干货集合

    Java多线程: java多线程详细总结:https://blog.csdn.net/chenruijia170707/article/details/78505351 ThreadLocal 用法及 ...

  8. js 跳转链接的几种方式

    1.跳转链接 在当前窗口打开 window.location.href="http://www.baidu.com" 等价于 <a href="baidu.com& ...

  9. 序列标注(BiLSTM-CRF/Lattice LSTM)

    前言 在三大特征提取器中,我们已经接触了LSTM/CNN/Transormer三种特征提取器,这一节我们将介绍如何使用BiLSTM实现序列标注中的命名实体识别任务,以及Lattice-LSTM的模型原 ...

  10. php基础——语法、变量

    一.php语法: 1.php语言需要写在<?php  ?>标签里面 2.php语言每行结束需要使用:作为结束符 3.php是一门弱语言,不要求先声明变量 4.可嵌套在HTML和js语言中 ...