原以为躲入代码世界,就可以不用搞关系,哪知“关系无处不在”。写代码多年之后,终于明白“面向对象的关键是搞好对象之间的关系”。而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)【转】的更多相关文章

  1. Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)【转载】

    Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一) 自从搞好了单向一对一关系,装满代码的心中塞进了挥之不去的情丝 —— 单相思.谁都知道音乐世界离不开情感,可谁又知 ...

  2. Entity Framework - 基于外键关联的单向一对一关系

    代码的世界,原以为世界关系很简单,确道是关系无处不在.NET世界里ORM框架中EntityFramework作为其中翘楚,大大解放了搬砖工作的重复工作,着实提高了不少生产力,而也碰到过不少问题!比如关 ...

  3. 【转】Entity Framework技术系列之7:LINQ to Entities

    前言 LINQ(Language Integrated Query,语言集成查询)是一组用于C#和VB.NET语言的扩展,它允许编写C#或者VB.NET代码,以与查询数据库相同的方式操作内存数据. L ...

  4. Entity Framework技巧系列之六 - Tip 20 – 25

    提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你 ...

  5. Entity Framework Core系列之DbContext(删除)

    上一篇我们介绍了Entity Framework Core系列之DbContext(修改),这一篇我们介绍下删除数据 修改实体的方法取决于context是否正在跟踪需要删除的实体. 下面的示例中con ...

  6. Entity Framework Core系列之DbContext(添加)

    上一篇我们介绍了Entity Framework Core系列之DbContext,对DbContext有了概念上的了解,这篇将介绍DbContext添加数据 通过DbContext添加实体的主要方法 ...

  7. 采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)

    前言 Entity Framework 延伸系列目录 今天来说说EF与MVC项目的性能检测和监控 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC Mi ...

  8. 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)

    前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...

  9. Entity Framework入门系列(1)-扯淡开篇

    这是我在Cnblogs上的第一个系列,但愿能坚持下去: 惯例索引 Entity Framework入门系列(1)-开篇兼索引: Entity Framework入门系列(2)-初试Code First ...

随机推荐

  1. [Oracle] - 性能优化工具(1) - AWR

    AWR快照 默认情况下,Oracle每隔一小时会自己主动产生一个快照,保存近期8天的快照. 我们能够通过例如以下语句获得产生快照的时间间隔和保存的天数: SYS@orcl(lx15)> sele ...

  2. mybatis11 sqlMapConfig.xml文件说明

    1sqlMapConfig.xml SqlMapConfig.xml中配置的内容和顺序如下: properties(属性) settings(全局配置参数) typeAliases(类型别名) typ ...

  3. LabVIEW系列——自定义错误

    1.自定义错误代码文本文件在labview中三处:      a).E:\Program Files\National Instruments\LabVIEW 8.6\project\errors   ...

  4. GUI编程笔记(java)01:GUI和CLI

    GUI Graphical User Interface(图形用户接口). 用图形的方式,来显示计算机操作的界面,这样更方便更直观. CLI Command line User Interface ( ...

  5. javascript 之闭包-理解不了来找我

    1,闭包是什么 (百度百科定义)--闭包是可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变 量).“闭包” 一词 ...

  6. [图文]centos6.3搭建FTP服务器教程

    我一开始是参照这个教程做的 http://www.linuxren.net/better/centos63-ftp.html 可是问题总是免不了的,我遇到几个问题. 一开始使用terminal的时候一 ...

  7. .NET平台下几种SOCKET模型的简要性能供参考

    转载自:http://www.cnblogs.com/asilas/archive/2006/01/05/311309.html .NET平台下几种SOCKET模型的简要性能供参考 这个内容在cnbl ...

  8. jmeter压测SSL加密网站

    1.生成秘钥文件 得到网站证书,用jdk自带的keytool生成秘钥文件,执行dos命令: keytool     -import -alias "aaa" -file " ...

  9. UDP—Socket,套接字聊天简单的聊天程序。

    思路:(发送端) 1.既然需要聊天.就应该怎么建立聊天程序,,DatagramSocket对象http://www.w3cschool.cc/manual/jdk1.6/ DatagramSocket ...

  10. Ubuntu常用终端快捷键

    CTRL+k:删除从光标到行尾的部分 CTRL+u:删除从光标到行首的部分 ALT+d:删除从光标到当前单词结尾的部分 CTRL+w:删除从光标到当前单词开头的部分 CTRL+a:将光标移到行首 CT ...