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

    附件http://files.cnblogs.com/xe2011/CSHARP_RichtextBox_PRINT.rar 打印 详情 http://support.microsoft.com/kb ...

  2. [TypeScript] Loading Compiled TypeScript Files in Browser with SystemJS

    TypeScript outputs JavaScript, but what are you supposed to do with it? This lesson shows how to tak ...

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

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

  4. linux安装php出现的各种错误解决方案

    configure: error: Try adding --with-zlib-dir=<DIR>. Please check config.log for more informati ...

  5. Android(java)学习笔记175:BroadcastReceiver之 外拨电话的广播接收者

    首先我们示例工程一览表如下: 1.首先我们还是买一个收音机,定义一个OutCallReceiver继承自BroadcastReceiver,onReceive()方法中定义了监听到广播,要执行的操作: ...

  6. Jquery调用从ashx文件返回的jsonp格式的数据处理实例

    开发环境:vs2010+jquery-1.4.min.js 解决问题:网上代码比较少,好多调试不通,返回数据不用json而用jsonp主要考虑解决跨域问题 开发步骤:打开VS2010,新建一web站点 ...

  7. Summary: How to calculate PI? Based on Monte Carlo method

    refer to: http://www.stealthcopter.com/blog/2009/09/python-calculating-pi-using-random-numbers/ Duri ...

  8. php字符串函数(1)

    下面去学习一下php的字符串函数,那么怎么去看手册呢,举个例子 int strcasecmp ( string $str1 , string $str2 ) 第一个int,表示此函数返回的类型是int ...

  9. BUG Error:Execution failed for task ':app:dexDebug'.

    Error:Execution failed for task ':app:dexDebug'. > com.android.ide.common.process.ProcessExceptio ...

  10. 对于EditText的详细用法

    EditText这个控件对于每一个Android开发者来说都是再熟悉不过了,但是,为什么有的人的EditText可以表现的那么好看,而刚入学Android的程序员来讲却丑到爆.这就充分的说明对于Edi ...