EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

时间:2024-05-16 23:07:02

鸡肋###

鸡肋(Chicken ribs),现代汉语词语,出自《三国志·魏书·武帝纪》裴松之注引《九州春秋》曰:“夫鸡肋,弃之如可惜,食之无所得,以比汉中,知王欲还也。”(食之无肉,弃之不舍) https://baike.baidu.com/item/鸡肋/206189

相信小伙伴们都知道这个词的典故。

那么,继续上一个彩蛋篇,让我来继续解释一下, 鸡肋乎 的说法,又是怎么一回事。

(本篇例子的源代码,可以在 https://github.com/kentliu2007/EFCoreDemo/tree/master/OwnedEntityTypes 下载。建议可以下载之后对照着代码来阅读本篇。我用的是VS2017)

为什么我们要做Table Split####

为什么我们要做Table Split?一切都是为了性能。

当今的关系型数据库产品,例如Oracle,或者SQL Server,哪怕你的Select命令,只返回一条记录里面的一个字段的内容,它的数据库引擎,都会从磁盘读取最小单位的块(Block)到内存里,然后再从内存里的N条记录中,过滤出你要的那条记录的那个字段,然后返回给你。

所以如果我们的数据表,每行的长度很长的情况下(例如拥有很多很多的字段,或者某几个字段特别的长),一个最小单位的块,能保存的记录行数就相对的少了。然后如果我们要查询和计算的记录行数比较多的话,数据库需要从磁盘读取的块,就相应地多了很多。从而影响性能。

针对这种情况,我们通常会对数据表进行纵向切割。对表的字段,按照某个方式,分门别类地,陆陆续续地把某些字段分离到另外一些表里面。例如,我们可以把存放着地址的那些长字段弄到另外一个表,当程序需要用到地址资料的时候才去访问这个表。这样,性能就提升上来了。

这个做法,就是Table Split。

EFCore Owned Entity Types对Table Split的支持####

如果看了上一篇,大家就可以知道,通过两个Entity,分别ToTable到两个不同的Table,且最后我们是统一通过Client这个类来访问所有属性,其实已经实现了对Table Split的支持。

当然,通过访问Client.ContactInfo属性才可以访问到电话号码、电子邮件,这样的方式有点不够便利。所以我们还可以进一步通过捣弄 getter/setter的把戏,直接通过Client.CellPhoneNo或者Client.Email的方式来获取联系资料。

具体请看下面的程序:

  • 项目

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇
  • 代码

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

看,这样处理之后,是不是很方便呢?

性能####

等等,刚才不是说了,做Table Split就是为了性能吗?主要是我们希望可以纵向切割数据表之后,程序在需要的时候,才去访问相应的数据表,通过这个做法来提升性能。例如如果程序没有需要访问地址资料的需求,那么就不会去访问存放地址资料的数据表。

Owned Entity Types有能够做到这个效果吗?让我们用SQL Server Profiler来监视一下,上面的程序,它是怎样访问数据表的。

上面的Unit Test程序,实际上,它发出是SQL命令如下:

EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

  • 前两条Insert命令,没毛病:

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇
  • 最后两条Delete,也没毛病

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇
  • 中间的那条Select命令,就透露着 诡异 了:

    EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    这个相当于在程序里面用了Include()命令的做法,是Eager Loading啊。

    那么没有实现程序在需要的时候,才去访问相应数据表的效果啊。

    其实我们是希望在程序运行到
var x = context.Clients.FirstOrDefault(e => e.Code == code);

的时候,它只访问Client表。

然后接着程序运行到

Assert.AreEqual(x != null && x.Email == email, true);

的时候,它才去访问ClientContactInfo表。

现在的情况是,当运行context.Clients.FirstOrDefault(e => e.Code == code);的时候,其实它就已经用Left Join的方式,把Client表和ClientContactInfo两个表都同时访问了。

所以如果用了Owned Entity Types来支持Table Split的话,对性能是没有提升的。

在我之前的EFCore系列的博客里,有一篇如何用EFCore Lazy Loading实现Entity Split。通过阅读那篇博客,你就可以看到,用EFCore的Lazy Loading,其实我们是可以用着同样的捣弄getter/setter的伎俩,通过walk around的形式来支持Table Split,同时实现了程序在需要的时候,才去访问相应数据表,从而达到原先希望提升系统性能的初衷。(当然,也建议你去下载之前的代码,自己去运行SQL Server Profiler来验证一下EFCore Lazy Loading的运行机制)

所以,对比着我的上一篇博客,我们回过头来看Owned Entity Types,同样是支持Table Split,但是用Lazy Loading的话有着更好的系统性能,那么,你有没有感觉到 鸡肋 的感觉呢?