【问题标题】:Entity Framework : Table per Concrete Type and unique IDs across tables实体框架:每个具体类型的表和跨表的唯一 ID
【发布时间】:2010-12-16 12:15:28
【问题描述】:

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

联系人(基础、抽象、未映射)
- 联系人ID
- 到其他表格(电子邮件、电话、..)的导航属性

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

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

现在,为了使这个工作正常,主键 (contactID) 在所有表中应该是唯一的。 然后有 2 个选项:
- GUID(不是粉丝)
- 一个额外的 DB 表生成身份(只有一个 ContactID 字段,派生表有 FK),这不会在 EF 中映射。

这个设置可行吗? 另外, ObjectContext 会发生什么? EF 在调用 SaveChanges 之前会生成什么样的临时密钥?它在对象之间是唯一的吗?

感谢您的任何想法。 迈克。

【问题讨论】:

    标签: sql inheritance entity-framework-4


    【解决方案1】:

    我们使用与以下数据库设计类似的构造:

    联系实体

    • 身份证

    联系可能性

    • 身份证
    • 位置
    • 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的有什么问题?

    Entity Framework 确实很好地支持了这种 db/class 设计,我们从这种设计中获得了很多乐趣。

    这个设置可行吗? 另外, ObjectContext 会发生什么? EF 在调用 SaveChanges 之前会生成什么样的临时密钥? 它在对象之间是唯一的吗?

    建议的设置非常可行! ObjectContext 运行良好,可以毫不费力地为派生类插入、更新和删除正确的表。临时钥匙?如果您使用派生类的 ID 模式(既是抽象类的主键又是外键),则不需要它们。使用 Guid,您可以非常确定它在不同的对象中是独一无二的。

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

    希望这会有所帮助...

    【讨论】:

    • 非常感谢,这正是我想要实现的设计。我有一个类似的“CP”表作为多对多表。另一个问题(实际上是一个复合问题..):您是否将基类(CP,CE)映射到它们的基础表?如果你这样做了,我想我们是在 TPT 设置中(不是 TPC)还是我错了?如果是 TPT,它不会产生带有很多联合的讨厌的 SQL 吗?谢谢。
    • (更新我的最后一个问题:我想这就是您在 CP 中的“ContactTypeID”作为基类中的鉴别器出现的地方?所以您不必合并所有联系可能性表来找出类型,对吧?)
    • 嗨,迈克,CP 表充当多对多表...在我的情况下,它充当 1(CE) 对多 (CP)。是的,我将(抽象)基类映射到它们的基础表,实际上 EntityModel 生成器为我做了这个;)是的,这是一个 Table-Per-Type 场景。此方案还提供了一些数据库优化,因为您必须跟踪较少的外键,并且使用其他 CP 或 CE“类型”更容易维护和扩展。
    • 是的,ContactTypeID 只是一个“查找”,具有 Private、Work 等值。还有关于讨厌的 SQL。我从来没有详细看过它,我相信微软在那里做得很好,在我的项目中,easy-coding 和 RAD 优先于性能。这不是一个高流量系统,所以我不太担心。
    【解决方案2】:

    (cmets 部分空间不足)

    我一直在进行一些测试。
    只要您只指定要查询的子类型(例如,在您的情况下为“地址”),您就可以了。
    但是,如果您查询基本类型(即使您不需要子类型信息),例如。只有 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;
    这将在联系人派生实体的所有表之间产生一个联合,以及性能问题:生成的 SQL 采用 ContactPossibility 表并连接到 ContactPossibilityID 上的地址,然后加入所有联系人派生表的联合,并与基本联系人表连接,在最终加入过滤的 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 表。

    迈克。

    【讨论】:

    • 请注意,相同查询的以下替代方法也可以正常工作: var address = from d in context.Contactpossibilities.OfType
      () join e in context.Contacts.OfType() on d.ContactID 等于 e.ContactID select d;
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2013-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多