(21)ASP.NET Core EF创建模型(关系)
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创建模型(关系)的更多相关文章
- (22)ASP.NET Core EF创建模型(索引、备用键、继承、支持字段)
1.索引 索引是跨多个数据存储区的常见概念.尽管它们在数据存储中的实现可能会有所不同,但也可用于基于列(或一组列)更高效地进行查找. 1.1约定 按照约定,将在用作外键的每个属性(或一组属性)中创建索 ...
- (20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)
1.必需和可选属性 如果实体属性可以包含null,则将其视为可选.如果属性的有效值不可以包含null,则将其视为必需属性.映射到关系数据库架构时,必需的属性将创建为不可为null的列,而可选属性则创建 ...
- (19)ASP.NET Core EF创建模型(包含属性和排除属性、主键、生成的值)
1.什么是Fluent API? EF中内嵌的约定将POCO类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体映射到约定指示外的其他对象,所以Fluent API和注解都是一种方法,这两种方法 ...
- C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制
在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Cachi ...
- asp.net core+ef core
asp.net core+ef core 官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一 ...
- 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 创 ...
- 使用 ASP.NET Core MVC 创建 Web API(二)
使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 六.添加数据库上下文 数据库上下文是使用Entity Framewor ...
- 使用 ASP.NET Core MVC 创建 Web API(四)
使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...
- 使用 ASP.NET Core MVC 创建 Web API——响应数据的内容协商(七)
使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...
随机推荐
- dart 大文件读取
dart 中不可避免会出现文件读取的情况, 甚至是很大的文件, 比如 200M 的文件 如果一次性读入内存,虽然也行得通, 但是如果在 flutter 中开启个 200M 大小的字节数组, 一不小心可 ...
- gitbook 入门教程之还在搞公众号互推涨粉?gitbook 集成导流工具,轻轻松松躺增粉丝!
相信大多数博客作者都或多或少有过这样想法: 现在各种平台这么多,想要实现全平台发布就要到处复制粘贴,等我有空一定做统一平台一次性全部解决! 不知道正在阅读文章的你,有没有这样的想法? 反正我确实这么想 ...
- mysql创建表时字段类型选择与优化
一.选择原则 1.应该尽量使用可以正确存储数据的最小字段类型 2.选用简单的数据类型,例如:一个是尽量用mysql内置的字段类型来存储日期和时间:另一个存储IP地址尽量用整型:能用整型的尽量不用字符串 ...
- SVN更改地址
因为服务器更改或其他某些原因导致svn地址改变,那么本地应该如何操作tortoiseSVN?如何成功的把项目进行迁移? 操作步骤 1.右击项目目录---TortoiseSVN----重新定位(英文版是 ...
- Apache Thrift 的基本使用
Apache Thrift 的基本使用 可以先看看官网是如何介绍的 The Apache Thrift software framework, for scalable cross-language ...
- 【django】分页
分页 1.简单分页 from django.conf.urls import url from django.contrib import admin from app01 import views ...
- 深入探究js中的隐式变量声明
前两天遇到的问题,经过很多网友的深刻讨论,终于有一个相对可以解释的通的逻辑了,然后我仔细研究了一下相关的点,顺带研究了一下js中的隐式变量. 以下文章中提到的隐式变量都是指没有用var,let,con ...
- spring boot 配置访问其他模块包中的mapper和xml
maven项目结构如下,这里只是简单测试demo,使用的springboot版本为2.1.3.RELEASE 1.comm模块主要是一些mybatis的mapper接口和对应的xml文件,以及数据库表 ...
- 阿里云服务器CentOS6.9 nexus私服使用
exus安装成功之后,我们开始使用. 登录nexus: 访问地址:http:ip地址:8081/nexus 默认登录账号:admin 密码:admin123 登录成功之后页面: 点击[Reposito ...
- JS/Jquery 表单方式提交总结
1. submit提交 (1). submit 按钮式提交 缺点:在提交前不可修改提交的form表单数据 // 1. html <form method="post" act ...