c# - Entity Framework 4 Table Per Hierarchy - 如何定义子项的导航属性?

标签 c# sql-server-2008 entity-framework entity-framework-4 table-per-hierarchy

我目前有一个 Entity Framework 4.0 模型和 Table Per Type (TPT),但存在一些性能问题(很多 LOJ/CASE 语句),以及两个特定域区域之间的问题映射(许多-对多)。

我决定试用 TPH。

我有一个名为“Location”的实体,它是抽象的,也是所有其他实体的基础。

然后我有 "Country", "City", "State", "Street",等等,它们都来自 Location。

LocationType”是鉴别器

那部分工作正常,但我在尝试为派生类型定义导航属性时遇到问题。

例如,“State”有一个“Country”,所以我应该可以这样做:

var state = _ctx.Locations.OfType<State>().Include("Country").First();
var countryForState = state.Country;

但这需要在“State”派生实体上有一个名为“Country”的导航属性。我该怎么做呢?当我从数据库生成模型时,我有一个表,所有 FK 都指向同一个表中的记录:

alt text

(注意:我在数据库中手动创建了这些 FK)。

但是 FK 作为导航放置在“Location”实体上,那么我如何将这些导航属性向下移动到派生实体?我无法复制粘贴导航,也无法“创建新的导航属性”,因为它不允许我定义开始/结束角色。

我们如何做到这一点?

TPH 也不清楚我们是否可以模型优先,或者我们必须从数据库开始,修复模型然后重新生成数据库。我还没有在 Internet 上找到关于如何为患有 TPH 的 child 定义导航的好例子。

注意:我不想代码优先。我当前的解决方案有带有 EDMX 的 TPT 和纯 POCO,我希望不影响域模型/存储库(如果可能),并且只更新 EF 模型/数据库。

编辑

仍然没有解决方案 - 但是我尝试先做模型,然后做添加 -> 新关联,这实际上允许我向派生实体添加导航。但是当我尝试“从模型生成数据库”时,它仍然会尝试为“Location_Street”、“Location_Country”等创建表。这几乎就像 TPH 不能首先完成模型一样。

编辑

这是我当前的模型:

alt text

我目前收到的验证错误:

Error 1 Error 3002: Problem in mapping fragments starting at line 359:Potential runtime violation of table Locations's keys (Locations.LocationId): Columns (Locations.LocationId) are mapped to EntitySet NeighbourhoodZipCode's properties (NeighbourhoodZipCode.Neighbourhood.LocationId) on the conceptual side but they do not form the EntitySet's key properties (NeighbourhoodZipCode.Neighbourhood.LocationId, NeighbourhoodZipCode.ZipCode.LocationId).

只是想我会继续编辑这个问题,编辑关于我目前所在的位置。我开始怀疑是否有可能使用自引用 FK 的 TPH。

编辑

所以我发现了上面的错误,那是因为我缺少 Neighbourhood-ZipCode 的连接表,多对多。

添加连接表(并将导航映射到该表)解决了上述错误。

但是现在我收到了这个错误:

Error 3032: Problem in mapping fragments starting at lines 373, 382:Condition members 'Locations.StateLocationId' have duplicate condition values.

如果我查看 CSDL,这里是“CountyState”的关联映射(一个州有多个县,一个县有 1 个州):

<AssociationSetMapping Name="CountyState" TypeName="Locations.CountyState" StoreEntitySet="Locations">
   <EndProperty Name="State">
      <ScalarProperty Name="LocationId" ColumnName="StateLocationId" />
   </EndProperty>
   <EndProperty Name="County">
      <ScalarProperty Name="LocationId" ColumnName="LocationId" />
   </EndProperty>
   <Condition ColumnName="StateLocationId" IsNull="false" />
</AssociationSetMapping>

提示的是 Condition ColumnName="StateLocationId",因为 ZipCodeState 关联也是这个条件。

但是我不明白。所有实体的鉴别器都是唯一的(我已经三重检查),我认为这是一个有效的场景:

  1. 县有一个州,由 StateLocationId 表示(位置表)
  2. ZipCode 有一个州,用 StateLocationId 表示(位置表)

这在 TPH 中无效吗?

最佳答案

所以我解决了一些问题,但我遇到了困难。

首先,当您在数据库端创建自引用 FK 时,当您尝试“从数据库更新模型”时, Entity Framework 会将这些导航属性添加到主要基类型,因为它没有明确的意义TPH - 您需要在模型端执行此操作。

但是,您可以手动将导航属性添加到子类型。

WRT 这个错误:

Error 3032: Problem in mapping fragments starting at lines 373, 382:Condition members 'Locations.StateLocationId' have duplicate condition values.

那是因为我有一个名为“Location_State”的 FK,我试图将其用于“ZipCode_State”关系和“City_State”关系 - 这不起作用(仍然不知道为什么)。

因此,为了解决这个问题,我必须添加额外的列和额外的 FK - 一个称为“ZipCode_State”,另一个称为“City_State” - 显然它必须是导航和物理 FK 之间的 1-1。

Location.LocationType has no default value and is not nullable. A column value is required to store entity data.

那是我的鉴别器字段。在数据库端,它是不可空

我阅读了有关此问题的帖子,他们说您需要将关系从 0..* 更改为 1..* - 但我的关系已经是 1..*。

如果您查看上面我的“位置”实际数据库表,所有 FK 都可以为空(它们必须是)。因此我开始怀疑我的关系是否应该是 0..*。

但由于 TPH,它们可以为空 - 并非所有“位置”都有“州”。但如果该位置是“城市”,那么它必须有一个“州”。

这个 SO 问题进一步安慰了我:ADO EF - Errors Mapping Associations between Derived Types in TPH

我实际上正在尝试该解决方法(在我遇到它之前),但该解决方法对我不起作用。我什至尝试将所有关系从 1..* 更改为 0..*,但仍然没有成功。

在这里浪费了太多时间,我回到了 TPT。

归根结底,如果使用 TPH,我会得到一个大得离谱的表,其中包含大量冗余的、可为 null 的列。 JOIN-wise,它更有效率。但至少对于 TPT,我不需要具有可为 null 和自引用的 FK。

如果有人能解决这个问题,请告诉我。但在那之前,我会坚持使用 TPT。

关于c# - Entity Framework 4 Table Per Hierarchy - 如何定义子项的导航属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4263546/

相关文章:

c# - DialogViewController 缺少后退按钮

sql - 删除重复数据的最快技术

sql - 如何在各种事件序列中找到第一个开始时间?

c# - 具有数据库事务的工作单元模式

c# - OData-v4 - 对实体集合进行操作然后执行函数

c# - 获取表中所有记录的样本

c# - 使用 get/set asp.net c# 属性太多

c# - 在 C# 中,连续 3 个 '/' 是做什么的?

c# - 如何在 RichTextBox C# 中显示行数

sql - 获取对象信息