Entity Framework - 理清关系 - 基于外键关联的单向一对一关系
注:本文针对的是 Entity Framework Code First 场景。
之前写过三篇文章试图理清Entity Framework中的一对一关系(单相思(单向一对一), 两情相悦(双向一对一), 两情相悦-续),但当时理得不够清,新的一年重新理一理。
当时“一对一”的实体关系,对应的数据库关系是外键关联(实际上是一种“一对多”关系,所以映射时用了WithMany)。而数据库中的“一对一”关系是共享主键(这是我个人的理解,不妥之处,欢迎指出),下篇文章将要理的就是这个关系。
由于双向“一对一”关系很少用到,而且不推荐使用,为了更清楚地理解,我们这里只谈单向一对一关系,也就是“基于外键关联的单向一对一关系(One-to-one Unidirectional relationships)”,对应的之前的文章是单相思(单向一对一)。
1. 类图

2. 类的定义

public class BlogSite { public int BlogID { get; set; } public string BlogApp { get; set; } public bool IsActive { get; set; } public Guid UserID { get; set; } public virtual BlogUser BlogUser { get; set; } }


public class BlogUser { public Guid UserID { get; set; } public string Author { get; set; } public int BlogID { get; set; } }

3. 数据库结构

4. Enitity Framework映射关系定义

protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<BlogSite>() .HasRequired(b => b.BlogUser) .WithMany(); }

怎么理解这里的HasRequired与WithMany呢?
我的理解是:HasRequired是针对BlogSite与BlogUser的关系,WithMany是针对BlogUser与BlogSite的关系。.HasRequired(b => b.BlogUser).WithMany()表示BlogSite与BlogUser存在Required关联关系(One-To-One, 每一个BlogSite都有一个对应的BlogUser),而这个关联对BlogUser来说是One-To-Many(一个BlogUser可以有多个BlogSite)。
你也许会疑惑?明明是单向一对一的实体关系,这里怎么弄出个一对多的关系?
如果有这样的疑惑,属正常现象,我从去年7月份写那篇文章开始疑惑,一直疑惑到现在,写篇文章时才有点搞明白。
注意,“单向一对一”是什么?是实体关系;Entity Framework是什么?是O/RM,是用来映射实体关系与数据库关系;还少了什么?数据库关系。
从上面的图中的数据库结构可以看出,BlogSite与BlogUser之间是外键关联关系,下面的图可以更清楚地看出这一点。

这个外键关联表示的就是BlogUser与BlogSite之间是一对多的数据库关系。
总结一下:
实体关系 —— BlogSite与BlogUser之间的单向一对一
数据库关系 —— BlogUser与BlogSite之间的一对多
是不是这样呢?Entity Framework是不是也是这样认为的呢?我们来验证一下。
怎么知道Entity Framework的想法呢?
通过EDM。
可这里是Code First?
不管什么First,都有EDM,因为这是Entity Framework的地图,没有它,Entity Framework就会晕头转向。Code First的EDM是在EF运行时生成的,不是没有地图,只是在Entity Framework的心中,我们看不到而已。
怎么让Entity Framework说出心里话呢?
从Morteza Manavi大师那学到一招,代码如下:

using (var context = new Context()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter writer = XmlWriter.Create(@"Model.edmx", settings)) { EdmxWriter.WriteEdmx(context, writer); } }

通过上面的代码,你就可以拿到EF心中的地图 —— edmx文件。请看地图:

果然,EF心中的地图就是BlogUser与BlogSite的一对多关系。地图的作用是什么?是让EF通过地图在数据库找到对应的数据。地图是如何产生的?是我们通过FluentAPI告诉Entity Framework:.HasRequired(b => b.BlogUser).WithMany()。
我们再来剖析一下.HasRequired(b => b.BlogUser).WithMany()。
之前,我一直被困扰,是因为总是把这里的定义当作实体的关系的定义。错!这里虽然用的是实体进行定义,但定义的是实体与数据库中的数据之间的映射关系(这本来就是常识,竟然被忽略了),更多的是告诉EF这些实体在数据库中的数据关系。EF最终是根据这个定义生成相应的SQL。
那我们从生成查询SQL的角度来理解一下:
.HasRequired(b => b.BlogUser)告诉EF,这是一个INNER JOIN查询(BlogSite INNER JOIN BlogUser);但INNER JOIN还需要条件,WithMany()告诉EF这是一个外键关联,EF据此进行推断,从BlogUser中找到主键UserID,并检查BlogSite中是否存在名为UserID的属性,如果存在,就以此为外键进行查询。而我们的BlogSite中有UserID,于是生成下面的SQL:

SELECT [Extent1].[BlogID] AS [BlogID], [Extent1].[BlogApp] AS [BlogApp], [Extent1].[IsActive] AS [IsActive], [Extent1].[UserID] AS [UserID], [Extent2].[UserID] AS [UserID1], [Extent2].[Author] AS [Author] FROM [dbo].[BlogSite] AS [Extent1] INNER JOIN [dbo].[BlogUser] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[UserID] WHERE 1 = [Extent1].[IsActive]

小结
理清“基于外键关联的单向一对一关系”,关键在于对modelBuilder.Entity<A>().HasRequired(A => A.B).WithMany()的理解。
我再来理解一次:
.HasRequired(A => A.B) 表示:1)实体A与实体B是一对一关系,实体A有一个导航属性A.B;2)在数据库中表A与表B存在一对一关联(INNER JOIN)。
.WithMany() 表示:1) 实体B与实体A可以没有关系,也可以是一对多关系;2)在数据库中表A与表B存在外键关联。
上面全是我的个人理解,真正理清Entity Framework中的关系需要大家的力量,我只是抛个砖。
除了“基于外键关联的单向一对一关系”,还有“基于共享主键的单向一对一关系”,这也是我们开发中经常碰到的一种关系,比如博客文章(BlogPost)与文章内容(PostBody),新闻(NewsItem)与新闻内容(NewsBody)。下一篇文章将会理理这个关系。
Entity Framework - 理清关系 - 基于外键关联的单向一对一关系的更多相关文章
- Entity Framework - 基于外键关联的单向一对一关系
代码的世界,原以为世界关系很简单,确道是关系无处不在.NET世界里ORM框架中EntityFramework作为其中翘楚,大大解放了搬砖工作的重复工作,着实提高了不少生产力,而也碰到过不少问题!比如关 ...
- Entity Framework Code First主外键关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework 实体间的外键关系
EF 默认是开户级联删除的,这此规则将会删除非空外键和多对多的关系,如果 在数据库上下文中的实体模型类 存在着 级联引用和多重删除路径,那么EF就抛出 级联引用和多重删除路径的异常. Introduc ...
- Hibernate,关系映射的多对一单向关联、多对一双向关联、一对一主键关联、一对一外键关联、多对多关系关联
2018-11-10 22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate ...
- 《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-13 过滤预先加载的实体集合 问题 你想过滤预先加载的实体集合,另外,你想使用 ...
- Hibernate 再接触 关系映射 一对一单向外键关联
对象之间的关系 数据库之间的关系只有外键 注意说关系的时候一定要反面也要说通 CRUD 数据库之间设计 主键关联 单向的外键关联 中间表 一对一单向外键关联 Husband.java package ...
- hibernate笔记--基于外键的单(双)向的一对一映射关系
假设我们有两张表,人员信息表Person,和身份信息表IdCard,我们知道每个人只有一个身份证号,所以这里的Person和IdCard表是一一对应的,也就是一对一的映射关系,基于外键的单向一对一映射 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (37) ------ 第六章 继承与建模高级应用之独立关联与外键关联
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-13 在基类中应用条件 问题 你想从一个已存在的模型中的实体派生一个新的实体, ...
- Entity FrameWork对有外键关联的数据表的添加操作
前天做了一个MVC Entity FrameWork项目,遇到有外键关联的数据编辑问题.当你编辑的时候,按照正常的逻辑,把每个字段的数据都对号入座了,然后点击保存按钮,本以为会顺理成章的编辑数据,但是 ...
随机推荐
- kafka单节点部署无法访问问题解决
场景:在笔记本安装了一台虚拟机, 在本地的虚拟机上部署了一个kafka服务: 写了一个测试程序,在笔记本上运行测试程序,访问虚拟机上的kafka,报如下异常: 2015-01-15 09:33:26 ...
- iOS - Swift available 平台判断
前言 Swift 语言中的 @available 和 #available,Swift 2.0 中,引入了可用性的概念.对于函数,类,协议等,可以使用 @available 声明这些类型的生命周期依赖 ...
- HIHO 线段树(单点)
#include <stdio.h> #include <string.h> #include <math.h> #include <iostream> ...
- JavaWeb学习总结(六)—HttpServletResponse
Response概述: response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse.在客户端发出每个请求时,服务 ...
- Android 项目中常用到的第三方组件
项目中常用到的第三方组件 1 社会化分享ShareSDK-Core-2.5.9.jarShareSDK-QQ-2.5.9.jarShareSDK-QZone-2.5.9.jarShareSDK-Sin ...
- Android 上千张图片的列表滑动加载
一般项目中图片加载用的比较多的是ImageLoader 但是需求自己配置一些参数 上手有些复杂 对于手机图库中有上千张图片需要加载时 一个使用性能很好的库Glide可以解决 效果图如下 滑动非常流畅 ...
- MyBatis——解决字段名与实体类属性名不相同的冲突
原文:http://www.cnblogs.com/xdp-gacl/p/4264425.html 在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况 ...
- 【转载】【Oracle 11gR2】db_install.rsp详解
[原文]http://blog.csdn.net/jameshadoop/article/details/48086933 ###################################### ...
- 转:C 函数调用栈
第一篇: 转自:http://kingj.iteye.com/blog/1555017 本文转自 http://blog.csdn.net/eno_rez/article/details/21586 ...
- C语言下动态库相互调用
前段时间需要完成多个模块业务,而这些模块的接口都是一样的,于是为了方便管理就把每个模块都根据接口封装成了SO库,这里就留下SO库调用样例 SO库源文件代码: //TestSo.c #include & ...