实体框架:每个具体类型的表和跨表的唯一ID

时间:2010-12-16 12:15:28

标签: sql inheritance entity-framework-4

我有一些只共享一些导航属性和ID的表。 我认为Table per Concrete类型继承在这里会很有趣..(?) 它看起来像这样:

联系(基础,摘要,未映射)
- ContactID
- 导航属性到其他表(电子邮件,电话,..)

Person:Contact(映射到具有各种属性的表Person + ContactID)
- 各种属性

公司:联系人(映射到具有各种属性+ ContactID的表公司)
- 各种属性

现在要使其工作,主键(contactID)在所有表中应该是唯一的。 2选项然后:
- GUID(不是粉丝)
- 另一个生成标识的数据库表(只有一个ContactID字段,派生表有FK),这不会映射到EF。

这个设置可行吗? 另外,ObjectContext会发生什么?在调用SaveChanges之前EF会生成什么样的临时密钥?它会在各种物体中独一无二吗?

感谢您的任何想法。 麦克

3 个答案:

答案 0 :(得分:1)

我们使用类似的结构和下面的数据库设计:

ContactEntity

  • ID

ContactPossibility

  • ID
  • 职位
  • ContactTypeID
  • ContactEntityID

地址

  • ID(= PK和FK到ContactPossibility.ID)

电话

  • ID(= PK和FK到ContactPossibility.ID)

  • ID(= PK和FK到ContactEntity.ID)

公司

  • ID(= PK和FK到ContactEntity.ID)
  • 名称

这导致实体模型有两个抽象类:ContactEntity(CE)& ContactPossibility(CP)和多个派生类(Address = CP,Email = CP,Person = CE,Company = CE)。抽象和派生类(db;中的行)共享相同的唯一标识符,因为我们在派生类中使用ID字段,该字段是抽象类主键的外键。我们为此使用Guid,因为我们的软件需要离线正常运行(不连接到主数据库),我们必须顺利处理同步问题。另外,Guid的问题是什么?

实体框架确实非常好地支持这种数据库/类设计,我们从这个设计中获得了很多乐趣。

  

这个设置可行吗?   另外,ObjectContext会发生什么?   在调用SaveChanges之前EF会生成什么样的临时密钥?   它会在各种物体中独一无二吗?

建议的设置非常可行! ObjectContext行为正常,将为派生类插入,更新和删除正确的表而不费力。临时钥匙?如果对抽象类的主键和外键使用派生类的ID模式,则不需要它们。对于Guid来说,你可以非常肯定这是独特的对象。

此外:从CP到CE的foreignKey将为每个CE(人员,公司,用户等)提供可跟踪的ContactPossibilities集合。这真的很酷,也很方便。

希望这会有所帮助......

答案 1 :(得分:1)

(评论部分没有足够的空间)

我一直在进行一些测试。
只要您只指定要查询的子类型(例如,在您的情况下为“地址”),就可以了。
但是,如果您查询基本类型(即使您不需要子类型信息),例如。只有ContactPossibility.ID,生成的SQL将UNION所有子类型表。
因此,查询“可跟踪”的ContactPossibilities集合可能会产生性能问题。

我尝试通过取消映射基本实体并将继承的实体拆分为自己的表+公共表来解决这个问题,基本上将TPT转换为TPC:从概念的角度来看这很好(在经过大量的edmx编辑之后) 。
直到我意识到这是愚蠢的...... :)确实在这种情况下你总是需要联合所有基础表来查询公共数据...
(虽然我不确定在本文末尾描述的情况,但没有继续测试它)

所以我想,因为大多数情况下我需要查询特定类型(人,公司,地址,电话......),现在就可以了,希望MS能够在EF4.5中修复。

所以我在查询时必须小心,另一个有趣的例子: 假设您要选择一个人然后查询他的地址,例如(试图按照您的命名): var person = from b in context.ContactEntities.OfType-Person-() where b.FirstName.StartsWith("X") select b;
var address = from a in context.ContactPossibilities.OfType-Address-() where **a.ContactEntity == person.FirstOrDefault()** select a;
这将在Contact派生实体的所有表和性能问题之间产生一个联盟:生成的SQL接受ContactPossibility表并加入ContactPossibilityID上的Address,然后加入与基本Contact表连接的所有Contact派生表的并集< / b>,在最终加入过滤后的Person表之前。

但是,请考虑以下备选方案:
var person = from b in context.ContactEntities.OfType-Person-() where b.FirstName.StartsWith("X")<BR> select b;
var address = from a in context.ContactPossibilities.OfType-Address-() where **a.ContactID == person.FirstOrDefault().ID** select a;
这样可以正常工作:生成的SQL接受ContactPossibility表并加入ContactPossibilityID上的Address,然后加入过滤后的Person表。

麦克

答案 2 :(得分:0)

干得好迈克!我会理解这个案子。并且肯定希望MS会有一些更优雅的做事方式。关于查询,我应该总是根据外键进行比较!

不得不给出另一个答案,因为没有足够的声誉来编辑你的答案:(