EF学习笔记5:怎样避免关系跨度
背景与动机
在上一篇博文EF行话中,我介绍了关系跨度(Relationship Span)的概念。
如果你记得关系跨度仅仅是对实体中缺少外键属性的一种补偿。
一个实体的关系跨度,让我们以StaffMember为例,可以确保Entity Framework知道与StaffMember有0..1关系的其它实体的键(EntityKey)(如DisciplineHistory)。
这些键很重要,没有它们Entity Framework不知道怎么删除或更新StaffMember(见提示7与提示9,有更多关于此概念的信息)。
目前通常0..1类型的关系是通过在数据库中由StaffMember表指向目标DisciplineHistory表的外键的外键来确立的。
在这种情况下关系跨度可以很容易的取得DisciplineHistory的键,因为我们可以进行"联合消除"。
虽然没有两个数据库完全一样,且完全有可能以完全不同的方式对相同的关系进行建模:你可以将FK放置在DisciplineHistorty表中。
一种实现这个目的的方法是使相关的DisciplineHistory表的PK与StaffMember的PK相同,换言之,PK即FK。事实上在数据库限制设计为1对0..1关系的情况下,这是EF支持的唯一方法。
这种方式最普遍用于对数据库建模时给实体添加额外的"方面"的情况。
在这些场合下,关系跨度开销有点偏大,因为Entity Framework不足以智能到知道怎么进行联合消除或这本就不可能(两个情况都是可能的)。
如果你更进一步推断这种情况,如有多个通过0..1关系关联的实体存在(例如StaffMember有许多如SalaryHistory,DisciplineHistory,AuditLog与BIO等),这时你会很容易发现,对StaffMember表进行一个简单查询的开销也将很大,这完全由于完成关系跨度操作的开销。
问题很明显…
怎样做来避免关系跨度?
这非常非常简单,你仅需要进行非跟踪(non-tracking)查询:
var source = ctx.Staff;
source.MergeOption == MergeOption.NoTracking;
var staff = (from s in source
where s.ID == 12
select s).First();
这样结果查询将不会"跨越"所有关联表如SalaryHistory,DisciplineHistory等中的信息。
不幸的是结果也不会"存在于"ctx(上下文)中。所以如果你打算使用这个结果,你不得不在手动附加一次。
在.NET 4.0中对这个问题有一种变通方法,称为FK联合,如果你使用FK联合代替独立联合则根本无需进行关系跨度。
对于一个短暂的上下文这很少引起问题,但是对于一个持续的上下文你必须小心同一个实体(担不是同一个对象)已经不再是上下文中之前查询所得到的结果了。同样如果你想要更新这个实体,你不能简单的使用这个实体来建立新的关系,而是要首先获取所有关联的实体的所有PK。
浙公网安备 33010602011771号