Entity Framework 实践系列 —— 搞好关系 - 单相思(单向一对一,one-to-one)【转】
原以为躲入代码世界,就可以不用搞关系,哪知“关系无处不在”。写代码多年之后,终于明白“面向对象的关键是搞好对象之间的关系”。而Entity Framework作为ORM中的明日之星,首当其冲的使命就是配合对象搞好关系。
博客园开发团队在使用Entit Framework的过程中,被困扰最多的就是实体类之间的关系处理以及这种关系与数据库之间的映射。所以,希望通过这个系列文章将我们的困惑、理解与实 践经验拿出来与大家分享。知识与经验只有拿出来分享与传播,才会变得更有价值;藏在那,只会慢慢变质,最终随风而去。
关系分三种:一对一、一对多、多对多。我们就从“一对一”开始吧。“一对一”说简单也简单,说复杂也复杂;在现实世界,关键在于是否找对了那个“一”,“找一个人,过一辈子”;而在代码世界,又该如何“一对一”,让我们一起探索吧。
“一对一”也分两种情况:一种是单相思(单向),一种是两情相悦(双向)。单相思容易,两情相悦难啊;但一见就能两情相悦毕竟可遇而不可求,多数时候两情相悦来自于一方的单相思。
那我们就从最简单的单相思开始吧 —— “我的眼中只有你,你的眼中会不会有我”。
场景描述:园子里的每一个“博客”(BlogSite)都对应着一个对技术充满激情的“人”(BlogUser)。我想参观一下园子,看看所有这些博客,看看这些博客背后的主人(GetAllBlogSites)。
类图如下:
数据库表结构如下:
VS2010中的项目文件结构(基于博客园最新架构):
开始我们的旅程:
1. 第一步当然是测试,TDD可不是用来炫耀的专业名词,它是一个轮子,可以让你在开发的道路上走得更快,所以叫测试驱动。测试代码如下:
- [TestMethod]
- publicvoid GetAllBlogSites_Test()
- {
- _aggBlogSiteService.GetAllBlogSites().ToList()
- .ForEach(
- b => { Console.WriteLine("BlogApp:"+ b.BlogApp +
- ", Author:"+ b.BlogUser.Author); }
- );
- }
测试代码很简单,就是调用应用层的服务接口IAggBlogSiteService.GetAllBlogSites();
通过测试代码我们可以明确需求:获取一个包含所有BlogSite的列表,每个BlogSite包含BlogUser信息。这也是一个很常见的查询需求。
2. 看一下应用层的服务实现:
- public IEnumerable<BlogSite> GetAllBlogSites()
- {
- return _blogSiteReposiotry.Entities
- .Include(b => b.BlogUser)
- .Where(b => b.IsActive ==true);
- }
很简单的LINQ查询,需要注意的地方是Include,只有用了Include,在执行时Entity Framework才会生成BlogSite与BlogUser两张表的INNER JOIN查询。否则,就会使用延迟加载(LazyLoading),在每次访问BlogSite.BlogUser属性时进行查询;如果返回100个 BlogSite,就会产生100次对BlogUser表的查询。
3. 进入关键环节,如何针对这种单向的一对一关系对Entity Framework进行设置(这里用的是Code First)。
有两种方式可以实现:一种是通过FluentAPI,在OnModelCreating中实现;一种是通过DataAnnotations直接在实体类上设置。
a) FluentAPI设置的具体代码如下:
- protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
- {
- modelBuilder.Entity<BlogUser>().ToTable("BlogUser");
- modelBuilder.Entity<BlogUser>().HasKey(u => u.UserID);
- modelBuilder.Entity<BlogSite>().ToTable("BlogSite");
- modelBuilder.Entity<BlogSite>().HasKey(b => b.BlogID);
- //针对“一对一”关系的设置
- modelBuilder.Entity<BlogSite>().HasRequired(b => b.BlogUser).
- WithMany().HasForeignKey(b => b.UserID);
- modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
- base.OnModelCreating(modelBuilder);
- }
只需看“一对一”关系设置部分的代码。HasRequired(b => b.BlogUser)很好理解就是定义“一对一”的关系;WithMany()目前是个迷,Many与One-to-One关系看起来驴头不对马嘴,但 就得这样,这可是我们经过很多尝试,最后试出来的解决方法。否则会生成让人哭笑不得的SQL语句。下面我们来欣赏一下EF生成的让人哭笑不得的SQL。
开始我们是用FluentAPI这样进行定义的:
- //针对“一对一”关系的设置
- modelBuilder.Entity<BlogSite>().HasRequired(b => b.BlogUser).
- WithRequiredDependent().Map(conf => conf.MapKey("UserID"));
注:这样定义时,需要注释掉BlogSite的UserID属性。
这种方式,能够得到正确的结果,但生成的SQL很奇怪:
- SELECT
- [Extent1].[BlogID] AS [BlogID],
- [Extent1].[BlogApp] AS [BlogApp],
- [Extent1].[IsActive] AS [IsActive],
- [Join1].[UserID1] AS [UserID],
- [Join1].[Author] AS [Author],
- [Join3].[BlogID] AS [BlogID1]
- FROM [dbo].[BlogSite] AS [Extent1]
- LEFT OUTER JOIN (SELECT [Extent2].[UserID] AS [UserID1], [Extent2].[Author] AS [Author]
- FROM [dbo].[BlogUser] AS [Extent2]
- LEFT OUTER JOIN [dbo].[BlogSite] AS [Extent3] ON [Extent2].[UserID] = [Extent3].[UserID] ) AS [Join1]
- ON [Extent1].[UserID] = [Join1].[UserID1]
- LEFT OUTER JOIN (SELECT [Extent4].[UserID] AS [UserID2], [Extent5].[BlogID] AS [BlogID]
- FROM [dbo].[BlogUser] AS [Extent4]
- LEFT OUTER JOIN [dbo].[BlogSite] AS [Extent5]
- ON [Extent4].[UserID] = [Extent5].[UserID] ) AS [Join3] ON [Extent1].[UserID] = [Join3].[UserID2]
- WHERE 1 = [Extent1].[IsActive]
无需多说,看一眼你就无法忍受这样的SQL。
再看看使用WithMany()生成的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]
多干净,这才是我们想要的。
b) 还有一种简单的方法,通过DataAnnotations在BlogSite的UserID属性上设置[ForeignKey("BlogUser")],代码如下:
- public class BlogSite
- {
- public int BlogID { get; set; }
- public string BlogApp { get; set; }
- public bool IsActive { get; set; }
- [ForeignKey("BlogUser")]
- public Guid UserID { get; set; }
- public BlogUser BlogUser { get; set; }
- }
这是Entity Framework 4.0开始引入的新特性,欲知详情,请看Foreign key vs. Independent associations in Entity Framework 4。
一个ForeignKey可以达到WithMany同样的效果,够简单。
写到这,单向一对一关系搞好咧。接着干吗呢?这还用问,运行测试,收工!
转自:http://www.cnblogs.com/dudu/archive/2011/07/07/entity_framework_one_to_one.html
Entity Framework 实践系列 —— 搞好关系 - 单相思(单向一对一,one-to-one)【转】的更多相关文章
- Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)【转载】
Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一) 自从搞好了单向一对一关系,装满代码的心中塞进了挥之不去的情丝 —— 单相思.谁都知道音乐世界离不开情感,可谁又知 ...
- Entity Framework - 基于外键关联的单向一对一关系
代码的世界,原以为世界关系很简单,确道是关系无处不在.NET世界里ORM框架中EntityFramework作为其中翘楚,大大解放了搬砖工作的重复工作,着实提高了不少生产力,而也碰到过不少问题!比如关 ...
- 【转】Entity Framework技术系列之7:LINQ to Entities
前言 LINQ(Language Integrated Query,语言集成查询)是一组用于C#和VB.NET语言的扩展,它允许编写C#或者VB.NET代码,以与查询数据库相同的方式操作内存数据. L ...
- Entity Framework技巧系列之六 - Tip 20 – 25
提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你 ...
- Entity Framework Core系列之DbContext(删除)
上一篇我们介绍了Entity Framework Core系列之DbContext(修改),这一篇我们介绍下删除数据 修改实体的方法取决于context是否正在跟踪需要删除的实体. 下面的示例中con ...
- Entity Framework Core系列之DbContext(添加)
上一篇我们介绍了Entity Framework Core系列之DbContext,对DbContext有了概念上的了解,这篇将介绍DbContext添加数据 通过DbContext添加实体的主要方法 ...
- 采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)
前言 Entity Framework 延伸系列目录 今天来说说EF与MVC项目的性能检测和监控 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC Mi ...
- 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)
前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...
- Entity Framework入门系列(1)-扯淡开篇
这是我在Cnblogs上的第一个系列,但愿能坚持下去: 惯例索引 Entity Framework入门系列(1)-开篇兼索引: Entity Framework入门系列(2)-初试Code First ...
随机推荐
- java-常用快捷键
alt+/:代码提示 ctrl+/:代码提示 ctrl+1:快速生成impl代码
- 微软称iPad版Office必来临 目前在完善中
[PConline 资讯]微软在今年6月份的时候面向iPhone发布了Office,同时需要用户注册Office 365.而鲍尔默今天在参加Garnter科技研讨会时透露,iPad版本的Office始 ...
- 利用Ajax把前端的数据封装成JSON格式发送到服务器端并写成XML格式在服务器的硬盘上
1.首先要在前端把要发送的东西(这里是一个实例化的car对象)都准备好,利用Ajax发送到服务器端,代码如下: <html xmlns="http://www.w3.org/1999/ ...
- android自定义TabWidget
在做项目的时候,需要用到这个选项卡,刚开始看了系统的tabwidget,囧了,底边有黑线不说,还不美观,扒了好多的网页发现前辈做的能够满足自己的需求,将代码修改了下,就能用喽,伟人说过,站在前辈的肩膀 ...
- InstallShield12豪华版破解版下载|InstallShield下载|软件打包工具
InstallShield 12 豪华版+破解版 下载 下载速度:220kb/s InstallShield 12为软件发行方提供率先的安装程序解决方式,可以制作强大可靠的Windows Instal ...
- xmemcached user guide --存档
XMemcached Introduction XMemcached is a new java memcached client. Maybe you don't know "memcac ...
- ubuntu权限管理常用命令 分类: linux ubuntu 学习笔记 2015-07-05 14:15 77人阅读 评论(0) 收藏
1.chmod 第一种方式 chomd [{ugoa}{+-=}{rwx}] [文件或者目录] u 代表该文件所属用户 g 代表该文件所属用户组 o 代表访客 a 代表所有用户 +-=分别表示增加权限 ...
- Linux bash常用测试判断选项
bash编程中if [ ]后面的测试选项: 1.整数测试: -le less equal -lt less than -ge greater equal -gt greater than -eq ...
- sql like '%x%'优化
好久没写点什么了.唉(此处省略无数,一切尽在苦逼中...) 说说sql中的全匹配优化吧.在sql server进行模糊查询的时候,如果是进行全匹配的话,那么肯定会用到like.我们知道like '%张 ...
- CSS排版页面
创建CSS文件如下: @charset "utf-8"; /* CSS Document */ *{ margin:0px; padding:0px; border:0px; } ...