2020-03-30 17:50 表之间的关系

关系

提纲: 比较多

  1. 关系\导航属性\自引用关系
  2. 每一个关系都会在相关实体中建立一个外键
  3. FluentApi 的作用(什么情况只能使用 FluentAPI)

术语

  • 主体实体: 这是包含主/备用键属性的实体。 有时称为关系的 "父项"。例如下面的 Blog
  • 主体健: 唯一标识主体实体的属性。 这可能是主键或备用健。例如下面Blog.BlogId
  • 相关实体(依赖实体): 这是包含外键属性的实体。 有时称为关系的 "子级"。例如下面的 Post 类
  • 外键: 用于存储相关实体的主体键值的依赖实体中的属性。例如下面 Post.BlogId
  • 导航属性: 在主体和/或从属实体上定义的属性,该属性引用相关实体。
    • 集合导航属性: 一个导航属性,其中包含对多个子实体的引用。 例如 Blog.Posts
    • 引用导航属性: 保存对单个子实体的引用的导航属性。 例如 Post.Blog
    • 反向导航属性: 讨论特定导航属性时,此术语是指关系另一端的导航属性。

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

关系

默认情况下,当在某个类型上发现导航属性时,将创建一个关系。

如何判断某一个属性是导航属性?
如果属性指向的类型不能由当前的数据库提供程序映射为标量类型,则该属性视为一个导航属性。

  • 完全定义关系
  • 单个导航属性

完全定义关系

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

  • 如果在两个类型之间找到一对导航属性,则这些属性将配置为同一关系的反向导航属性
  • 如果依赖实体包含名称与下面其中一种模式相匹配的属性,则该属性将被配置为外键
    • <navigation property name><principal key property name>, 导航属性名称+主实体主键名称,例如 XBlogBlogId, XBlog 为导航属性名称
    • <navigation property name>Id, 导航属性名称+Id,例如 XBlogId
    • <principal entity name><principal key property name>, 主实体名称+主实体主键名称,例如 BlogBlogId
    • <principal entity name>Id, 主实体名称+Id,例如 BlogId

如果找不到和上面所匹配的属性并且没有使用 FluentApi 定义外键字段时,则 EFCORE 将自动产生一个外键属性
属性名称为:

  • <navigation property name><principal key property name> ( 导航属性名称+主表主键名称) 或
  • <principal entity name><principal key property name> ( 主体类名称+主表主键名称)

单个导航属性

  • 包含一个导航属性(无反向导航,没有外键属性) 或者

例如, Blog中有 public List<Post> Posts { get; set; } , 而 Post中没有任何指向 Blog 的属性

  • 包含一个导航属性(无反向导航) 和一个外键属性。

例如, Post中有 public Blog Blog { get; set; } , 而 Blog 中没有任何指向 Post 的属性

如上所述,也将产生一个外键属性, 此外键属性为隐藏外键属性

FluentApi中的方法:

使用 FluentApi 一般情形下没有必要,因为 EFCore 会自动寻找关系,并在相关表中产生外键.
除非:

  1. 需要自己指定关联关系的属性名称 (如果存在此属性)

使用 BlogForeignKey 属性作为关联字段
[ForeignKey("BlogForeignKey")] public Blog Blog { get; set; } 或者
modelBuilder.Entity<Post>() .HasOne(p => p.Blog) .WithMany(b => b.Posts) .HasForeignKey(p => p.BlogForeignKey);

  1. 关联关系为一个影子外键, 即列在类中不存在,但是在表中存在

列 BlogForeignKey 在 Post 中没有对应的属性,

modelBuilder.Entity<Post>().Property<int>("BlogForeignKey");
modelBuilder.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts).HasForeignKey("BlogForeignKey");
  1. 需要自己指定外键的约束名称

modelBuilder.Entity<Post>() .HasOne(p => p.Blog) .WithMany(b => b.Posts) .HasForeignKey(p => p.BlogId) .HasConstraintName("ForeignKey_Post_Blog");

  1. 关联到组合主键
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 });

5 没有导航属性,也需要建立关系

Post 和 Blog 2个类相互间没有任何关联关系
modelBuilder.Entity<Post>() .HasOne<Blog>() .WithMany() .HasForeignKey(p => p.BlogId);

  1. 关联到非主键 ( 备用健 )

modelBuilder.Entity<RecordOfSale>() .HasOne(s => s.Car) .WithMany(c => c.SaleHistory) .HasForeignKey(s => s.CarLicensePlate) .HasPrincipalKey(c => c.LicensePlate);

  1. 一对一时,需要使用 FluentApi 定义主表

modelBuilder.Entity<Blog>() .HasOne(b => b.BlogImage) .WithOne(i => i.Blog) .HasForeignKey<BlogImage>(b => b.BlogForeignKey);

  1. 一个表存在对另一个表的多个关系
  • HasOne,WithOne 用于引用导航属性
  • HasMany,WithMany 用于集合导航属性
  • HasOne/WithMany 定义了一对(一对多)反向导航属性
  • HasMany/WithOne 定义了一对(一对多)反向导航属性
  • HasOne/WithOne 定义了一对(一对一)反向导航属性
  • 以上均返回 CollectionNavigationBuilder<TEntity, TRelatedEntity> (导航的构建类)
  • WIthMay , WithOne 定义在类中 CollectionNavigationBuilder<TEntity, TRelatedEntity>, 返回如下的实例

ReferenceCollectionBuilder<Blog, Post> 实例 ( 一对反向导航的构建类 )

下面代码都定义一对反向导航关系,返回 ReferenceCollectionBuilder<Blog, Post> 实例

modelBuilder.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts);
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(x => x.Blog);

下面代码定义了 2个单向导航属性

返回 ReferenceCollectionBuilder<Blog, Post> 实例

表示 Blog 中有 List<Post> posts 属性, 而 Post 中没有 指向 Blog 的 属性
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne();
表示 Post 中有 Blog blog 属性, 而 Blog 中没有 指向 Post 的 属性
modelBuilder.Entity<Post>().HasOne(b => b.Blog).WithMany();

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352