• Entity Framework 实践系列 —— 搞好关系


    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

  • 相关阅读:
    无向图判断割点
    C
    连通图 求至少有给几个点信息才能传遍全图,至少添加几条边才能使全图联通
    线段树区间更新(set暴力)
    A
    辗转相除法(数学推理)
    Python List index()方法
    Python List extend()方法
    Python List count()方法
    Python List append()方法
  • 原文地址:https://www.cnblogs.com/lxiang/p/3364081.html
Copyright © 2020-2023  润新知