Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)

自从搞好了单向一对一关系,装满代码的心中塞进了挥之不去的情丝 —— 单相思。谁都知道音乐世界离不开情感,可谁又知道代码世界同样需要情感。

单相思是星星之火,它存在的唯一目的是点燃两个人的世界。让我们紧握心中的火苗,开始两情相悦的征途吧。

先回顾一下单相思的场景:

BlogSite单相思BlogUser。

BlogSite样子:


publicclass BlogSite
{
publicint BlogID { get; set; }
publicstring BlogApp { get; set; }
publicbool IsActive { get; set; }
public Guid UserID { get; set; }
public virtual BlogUser BlogUser { get; set; }
}

BlogUser样子:

publicclass BlogUser
{
public Guid UserID { get; set; }
publicstring Author { get; set; }
}

OnModelCreating中的定义:

modelBuilder.Entity<BlogSite>().HasRequired(b => b.BlogUser)
.WithMany().HasForeignKey(b => b.UserID);

可以看出,现在的情形是BlogSite的心中有BlogUser,BlogUser的心中没有BlogSite。

要让两情相悦,只要BlogSite能打动BlogUser,让BlogUser心中有他。在现实世界这谈何容易,但在代码世界你可以随心所欲。我们可以先强行让BlogUser心中有BlogSite,代码如下:

publicclass BlogUser
{
public Guid UserID { get; set; }
publicstring Author { get; set; }
publicvirtual BlogSite BlogSite { get; set; }
}

运行一下测试,看会发生什么?

会不会是这样?BlogUser抽BlogSite两巴掌:“这些年你TMD死哪里去了。。。 ”

代码世界可没有现实世界那样“暴力”,只是返回一个异常:

System.Data.SqlClient.SqlException: Invalid column name 'BlogSite_BlogID'.

生成的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],
[Extent3].[BlogSite_BlogID]AS[BlogSite_BlogID]
FROM[dbo].[BlogSite]AS[Extent1]
INNERJOIN[dbo].[BlogUser]AS[Extent2]
ON[Extent1].[UserID]=[Extent2].[UserID]
LEFTOUTERJOIN[dbo].[BlogUser]AS[Extent3] 
ON[Extent1].[UserID]=[Extent3].[UserID]
WHERE1=[Extent1].[IsActive]

两情相悦的确不容易,刚想悦一下就被拒绝了,而且是莫明的理由。

别泄气,解决问题和追女孩一样,要有一种锲而不舍的精神。

别着急,先让自己静下来,来一杯咖啡,或者写写博客。。。让问题在思维中浸泡一会。。。

浸泡之后,马上回来。

不要急于去找答案,而是要先进一步明确问题,既然是搞关系,就要仔细分析一下BlogSite与BlogUser之间的关系。

看类图:

BlogSite有一个属性BlogUser,BlogUser有一个属性BlogSite;假如BlogSite是男人,BlogUser是女 人,那么通过这两个类的定义,我们知道了(当然EF也知道了)  —— 男人可以娶女人,但只能娶一个;女人可以嫁给男人,但只能嫁一个。

看数据库表结构:

BlogSite表有个UserID字段对应BlogUser的UserID主键。所以,一个BlogSite找对应的BlogUser很容易,拿 着自己知道的UserID直接在BlogUser表中找出自己的另一半;而一个BlogUser找对应的BlogSite就难一些,先通过自己的 UserID在BlogSite表中找到对应的BlogID,然后通过BlogID找到对应的BlogSite。

打个比方,两情相悦的爱情密码藏在男人心里,男人一眼就能看出属于自己的女人,而女人需要先找出男人心里的爱情密码,然后看这个密码是不是自己。难怪男人要主动追求女人。

另外,由于BlogSite表的UserID字段不能为空,所以男人不能没有女人,也就是男人依赖(Dependent)女人;BlogUser表中没有BlogID,女人是主角(Principal),是等着男人来追求的。

通过上述的分析,我们可以理出这样的关系:

男人(BlogSite)需要(HasRequired)女人(BlogUser),女人也需要女人;男人通过爱情密码(UserID)找到属于自 己的女人,并依赖她(WithRequiredDependent);女人通过爱情密码(UserID)确定她可以主宰 (WithRequiredPrincipal)的男人。

有了这样的关系描述,我们可以在EF中通过Fluent API写出来,有两种写法,效果一样:

写法一(出自男人之手):

modelBuilder.Entity<BlogSite>().HasRequired(b => b.BlogUser)
.WithRequiredDependent(u => u.BlogSite).Map(conf => conf.MapKey("UserID"));

写法二(出自女人之手):

modelBuilder.Entity<BlogUser>().HasRequired(u => u.BlogSite)
.WithRequiredPrincipal(b => b.BlogUser).Map(conf => conf.MapKey("UserID"));

让我们测试一下,看看他们是否真的两情相悦。测试代码如下:


[TestMethod]
publicvoid GetAllBlogSites_Test()
{
_aggBlogSiteService.GetAllBlogSites().ToList()
.ForEach(
b => { Console.WriteLine("BlogApp:"+ b.BlogUser.BlogSite.BlogApp +
", Author:"+ b.BlogUser.BlogSite.BlogUser.Author); }
);
}

看看红色字体部分,测试的就是是否“你中有我,我中有你”。

在测试之前,我们需要将爱情密码隐藏,也就是把BlogSite的UserID属性注释掉。不然会出现错误 —— Each property name in a type must be unique. Property name 'UserID' was already defined.

运行测试,爱情大考验:

pass! 爱情测试通过,可以步入婚姻的殿堂。。。

相爱容易,相处难,婚姻生活才是对爱情的真正考验。

代码世界也是一样,测试通过了,但背后的代码是否以我们期望的方式运行呢?

打开Server Server Profiler,看个究竟:

当我们获取一个BlogSite列表时,实际执行的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]
LEFTOUTERJOIN (SELECT[Extent2].[UserID]AS[UserID1], [Extent2].[Author]AS[Author]
FROM[dbo].[BlogUser]AS[Extent2]
LEFTOUTERJOIN[dbo].[BlogSite]AS[Extent3]
ON[Extent2].[UserID]=[Extent3].[UserID] ) AS[Join1]
ON[Extent1].[UserID]=[Join1].[UserID1]
LEFTOUTERJOIN (SELECT[Extent4].[UserID]AS[UserID2], [Extent5].[BlogID]AS[BlogID]
FROM[dbo].[BlogUser]AS[Extent4]
LEFTOUTERJOIN[dbo].[BlogSite]AS[Extent5]
ON[Extent4].[UserID]=[Extent5].[UserID] ) AS[Join3]
ON[Extent1].[UserID]=[Join3].[UserID2]
WHERE1=[Extent1].[IsActive]

当我们获取一个BlogUser列表时,实际执行的SQL是:


SELECT
1AS[C1],
[Extent1].[UserID]AS[UserID],
[Extent1].[Author]AS[Author],
[Extent3].[BlogID]AS[BlogID],
[Extent3].[BlogApp]AS[BlogApp],
[Extent3].[IsActive]AS[IsActive],
[Extent4].[UserID]AS[UserID1]
FROM[dbo].[BlogUser]AS[Extent1]
LEFTOUTERJOIN[dbo].[BlogSite]AS[Extent2]ON[Extent1].[UserID]=[Extent2].[UserID]
LEFTOUTERJOIN[dbo].[BlogSite]AS[Extent3]ON[Extent2].[BlogID]=[Extent3].[BlogID]
LEFTOUTERJOIN[dbo].[BlogSite]AS[Extent4]ON[Extent2].[BlogID]=[Extent4].[BlogID]

看到这样的SQL,你也许会感叹:为了两情相悦,付出这么大的代价,值得吗?

值得!目前的代价只是暂时的,两情相悦,共同努力,一切都可以改变!

这个SQL的问题目前还没找到解决方法,先放着,随着博客园团队的成长,一定会解决这个问题!

更新1:17:30左右找到SQL问题的解决方法,下一篇文章的内容就是这个。

更新2:SQL生成问题的解决方法见Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)- 续

转自:http://www.cnblogs.com/dudu/archive/2011/07/08/entity_framework_one_to_one_two_way.html

Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)【转载】的更多相关文章

  1. Entity Framework 实践系列 —— 搞好关系 - 单相思(单向一对一,one-to-one)【转】

    原以为躲入代码世界,就可以不用搞关系,哪知“关系无处不在”.写代码多年之后,终于明白“面向对象的关键是搞好对象之间的关系”.而Entity Framework作为ORM中的明日之星,首当其冲的使命就是 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. Entity Framework Plus 系列目录

    Entity Framework Plus 系列文章计划的已经全部写完,可能还有其他功能没有写到,希望大家能够多动手,尝试一下使用,一定会给您带来一些帮助的.文章全部写完,也应该出一个目录方便查看,目 ...

随机推荐

  1. 理解标签重置reset

    /*样式表文件来自 懒人css http://lrcss.lrjz100.com *//*---------重置---------*/html{font-size: 100%;-webkit-text ...

  2. Android开发_后台任务task管理_allowTaskReparenting alwaysRetainTaskState clearTaskOn

    1.android:allowTaskReparenting 这个属性用来标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个task移动到有共同affinity的task,“tru ...

  3. cocos2d-x 2.0.3 设置高清模式注意事项(已移除-hd方式)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=304 在cocos2d-x 2. ...

  4. Mybatis高级应用

    Mybatis是一个半自动的框架.相对于hibernate全自动模式,mybatis为开发人员提供了更加灵活的对sql语句操作的控制能力,有利于dba对相关的sql操作进行优化,同时也方便开发者构建复 ...

  5. virtualbox 安装windows系统的一些问题

    今天总结一下,使用virtualbox安装windows系统的一些问题. 安装的是Ghost的系统,正版系统也可以参考. 首先本人的机器原系统是ubuntu 16.04 LTS x64 1.win7或 ...

  6. 8个开发必备的PHP功能(转)

    又是好几天没写博客,今天看到了个不错的文章,就转载到自己的博客,好以后查询方便. 1.传递任意数量的函数参数 我们在.NET或者JAVA编程中,一般函数参数个数都是固定的,但是PHP允许你使用任意个数 ...

  7. C#一般处理程序获取Session

    如果需要用ajax去动态校验验证码,如何获取Session保存的值呢? 你需要做两步: 一.在你的一般处理程序中添加命名空间 (using System.Web.SessionState;) 二.在你 ...

  8. 2015前端各大框架比较(angular,vue,react,ant)

    前端流行框架大比拼 angular vue react ant-design angularjs angular是个MVVM的框架.针对的是MVVM这整个事.angular的最主要的场景就是单页应用, ...

  9. php 半角与全角相关的正则

    半角的全部确认,带小初音的确认法则 ^[ア-ン゙゚ァ-ョッヲー -]+$ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset// ...

  10. [压缩解压缩] SharpZip--压缩、解压缩帮助类

    里面有三个类都是用于压缩和解压缩的.大家看下图片 看下面代码吧 /// <summary> /// 类说明:SharpZip /// 编 码 人:苏飞 /// 联系方式:361983679 ...