原以为躲入代码世界,就可以不用搞关系,哪知“关系无处不在”。写代码多年之后,终于明白“面向对象的关键是搞好对象之间的关系”。而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. DocX组件读取与写入Word

    本文转载:http://www.cnblogs.com/yanweidie/p/3861482.html 由于上周工作比较繁忙,所以这篇文章等了这么久才写(预告一下,下一个章节正式进入NVelocit ...

  2. [Redux] Filtering Redux State with React Router Params

    We will learn how adding React Router shifts the balance of responsibilities, and how the components ...

  3. 导入一个AndroidStudio工程作为一个Library Module

    尊重劳动成果,转载请注明出处:http://blog.csdn.net/growth58/article/details/47441245 关注新浪微博:@于卫国 邮箱:yuweiguocn@gmai ...

  4. SQLServer怎样导入excel

    --从Excel文件里,导入数据到SQL数据库中,非常easy,直接用以下的语句: /*======================================================== ...

  5. MYSQL 系统命令 源码定位

    sql_cmd.h enum enum_sql_command { SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_AL ...

  6. Oracle学习----集群因子(Clustering Factor)

    1.集群因子的算法: 通过dbms_rowid.rowid_block_number(rowid)找到记录对应的block 号.索引中记录了rowid,因此oracle 就可以根据索引中的rowid来 ...

  7. Linux服务器常用性能监控命令汇总

    1.ifconfig 网卡数目.ip地址.Mac地址.MTU大小 eth0 Link encap:Ethernet HWaddr 00:0d:3a:50:12:e9 inet addr:10.0.0. ...

  8. JDK1.8聚合操作

    在java8 JDK包含许多聚合操作(如平均值,总和,最小,最大,和计数),返回一个计算流stream的聚合结果.这些聚合操作被称为聚合操作.JDK除返回单个值的聚合操作外,还有很多聚合操作返回一个c ...

  9. J2EE入门必备

    1,J2EE是什么 J2EE(Java 2 platform Enterprise Edition)是软件平台,适于创建服务器端的大型应用软件和服务系统. J2EE适合开发大规模的业务系统,这种级别的 ...

  10. 1. 连接字符串的创建 - Lazy.Framework从零开始设计自己的ORM架构

    开发初衷 注册了博客园已经有几个月了,却从来都没有上来过,本人大概从2010年开始就开始做.NET 方向的开发. 这个是我在博客园发布的第一个帖子. 主要就是说说最近在写的一个ORM架构. 本人接触的 ...