实体框架自引用分层多对多

Posted

技术标签:

【中文标题】实体框架自引用分层多对多【英文标题】:Entity Framework Self Referencing Hierarchical Many To Many 【发布时间】:2010-03-02 23:26:30 【问题描述】:

好的,这个问题解决了。

从概念上讲,我有一个资源实体,它可以有许多子资源和许多父资源。 Resource 表有两个字段,ID 和 Name,ID 是主键。

为了完成多对多关系,我创建了一个 ResourceHierarchy 表,它有两个字段(Parent_ID,Child_ID)和两个外键,每个外键都引用 Resource 表 (ID) 的主键,ResourceHierarchy 表有一个复合表(Parent_ID, Child_ID)的主键

现在我们已经知道每个资源都可以作为其他资源的父级或子级,但是从逻辑上讲,并非所有资源都会有父级,但这不是重点。例如,假设我的资源表中有以下资源。

ID    Name
10000 Little House
10001 Font Door
10002 Roof
10003 Roof Tile
10004 Tile Monster

在 ResourceHierarchy 表中,我们有以下关系。

Parent_ID Child_ID
10000     10001
10000     10002
10002     10003
10004     10003

然后Entity Framework生成Entity,到目前为止一切顺利……

如果您要检查 edmx 文件中生成的代码,您会看到 ResourceHierarchy 表被视为关系,并且 ResourceHierarchy 表无法通过代码访问,因为它没有被视为实体。

如果这就是我想要的,那么它会很完美。

但是,当我想将数量列添加到资源实体层次结构时,问题就开始了。例如,小房子只有一个前门和一个屋顶,但屋顶和瓷砖怪物资源可以有许多屋顶瓦片。

因此,如果我们在 Resource 表中添加一个 Quantity 列,那么我们会得到以下结果。

ID    Name            Quantity
10000 Little House 1
10001 Font Door  1
10002 Roof   1
10003 Roof Tile  5
10004 Tile Monster 1

这就产生了屋顶和瓦片怪物必须共享 5 个屋顶瓦片的问题。所以很自然地,我会尝试将数量列添加到 ResourceHierarchy 表中,但是一旦我这样做并刷新生成的代码,现在将 ResourceHierarchy 表视为一个实体,而不是以前的关系。现在为了回到资源表,我必须通过这个非概念性的“实体/关系”,这不是很直接。就像我的概念模型中有一个实体,它只用于遍历资源实体,我什至不确定 Resource.Children.Add(r) 是否会在数据库的 ResourceHierarchy 表中创建新行.就像我会从我仅用作关系的实体中挑选属性,即数量。

理想情况下,ResourceHierarchy 表的 Quantity 列应如下所示。

Parent_ID Child_ID Quantity
10000     10001  1
10000     10002  1
10002     10003  8
10004  10003  13

并且资源实体仍将具有子项、父项导航属性,并且以某种方式访问​​数量列作为资源实体的属性。

我尝试将生成的代码从有数量列和没有数量列合并,但引发异常,我将其解释为 ResourceHierarchy 表可以是关系或实体,但不能两者兼而有之。

p>

请帮忙!

随着 db 中 ResourceHierarchy 表中数量列的添加和排除,edmx 发生了巨大变化。

这里是一个示例比较,唯一的区别是 Resource 是 ResourceType 而 ResourceHierarchy 是 ResourceTypeHierarchy。

SSDL 存储模型除了 ResourceTypeHierarchy EntityType 中的一个额外属性外没有任何变化,所以我不会在下面包含它。

在资源类型层次结构中没有数量列

资源类型层次结构是一种关系

<!-- CSDL content -->
        <edmx:ConceptualModels>
          <Schema Namespace="MyModel" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
            <EntityContainer Name="MyEntities">
              <EntitySet Name="ResourceTypes" EntityType="MyModel.ResourceType" />
              <AssociationSet Name="ResourceTypeHierarchy" Association="MyModel.ResourceTypeHierarchy">
                <End Role="ResourceType" EntitySet="ResourceTypes" />
                <End Role="ResourceType1" EntitySet="ResourceTypes" /></AssociationSet></EntityContainer>
            <EntityType Name="ResourceType">
              <Key>
                <PropertyRef Name="ID" /></Key>
              <Property Type="Int32" Name="ID" Nullable="false" />
              <Property Type="String" Name="Type" Nullable="false" MaxLength="25" FixedLength="false" Unicode="false" />
              <NavigationProperty Name="Parents" Relationship="MyModel.ResourceTypeHierarchy" FromRole="ResourceType" ToRole="ResourceType1" />
              <NavigationProperty Name="Children" Relationship="MyModel.ResourceTypeHierarchy" FromRole="ResourceType1" ToRole="ResourceType" /></EntityType>
            <Association Name="ResourceTypeHierarchy">
              <End Type="MyModel.ResourceType" Role="ResourceType" Multiplicity="*" />
              <End Type="MyModel.ResourceType" Role="ResourceType1" Multiplicity="*" /></Association></Schema>
        </edmx:ConceptualModels>


<!-- C-S mapping content -->
        <edmx:Mappings>
          <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
            <EntityContainerMapping StorageEntityContainer="MyModelStoreContainer" CdmEntityContainer="MyEntities">
              <EntitySetMapping Name="ResourceTypes">

                <EntityTypeMapping TypeName="IsTypeOf(MyModel.ResourceType)">
                  <MappingFragment StoreEntitySet="ResourceType">
                    <ScalarProperty Name="ID" ColumnName="ID" />
                    <ScalarProperty Name="Type" ColumnName="Type" /></MappingFragment></EntityTypeMapping></EntitySetMapping>
              <AssociationSetMapping Name="ResourceTypeHierarchy" TypeName="MyModel.ResourceTypeHierarchy" StoreEntitySet="ResourceTypeHierarchy">
                <EndProperty Name="ResourceType1">
                  <ScalarProperty Name="ID" ColumnName="Parent_ID" /></EndProperty>
                <EndProperty Name="ResourceType">
                  <ScalarProperty Name="ID" ColumnName="Child_ID" /></EndProperty></AssociationSetMapping></EntityContainerMapping>
          </Mapping>
        </edmx:Mappings>

在资源类型层次结构上有数量列

RESOURCETYPEHIERARCHY 现在是一个实体

<!-- C-S mapping content -->
<edmx:Mappings>
  <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
    <EntityContainerMapping StorageEntityContainer="MyModelStoreContainer" CdmEntityContainer="MyEntities">
      <EntitySetMapping Name="ResourceTypes">

        <EntityTypeMapping TypeName="IsTypeOf(MyModel.ResourceType)">
          <MappingFragment StoreEntitySet="ResourceType">
            <ScalarProperty Name="ID" ColumnName="ID" />
            <ScalarProperty Name="Type" ColumnName="Type" /></MappingFragment></EntityTypeMapping></EntitySetMapping>
      <EntitySetMapping Name="ResourceTypeHierarchies">

        <EntityTypeMapping TypeName="IsTypeOf(MyModel.ResourceTypeHierarchy)">
          <MappingFragment StoreEntitySet="ResourceTypeHierarchy">
            <ScalarProperty Name="Child_ID" ColumnName="Child_ID" />
            <ScalarProperty Name="Parent_ID" ColumnName="Parent_ID" />
            <ScalarProperty Name="Quantity" ColumnName="Quantity" /></MappingFragment></EntityTypeMapping></EntitySetMapping>
      <AssociationSetMapping Name="FK_Child" TypeName="MyModel.FK_Child" StoreEntitySet="ResourceTypeHierarchy">
        <EndProperty Name="ResourceTypeHierarchy">
          <ScalarProperty Name="Child_ID" ColumnName="Child_ID" />
          <ScalarProperty Name="Parent_ID" ColumnName="Parent_ID" /></EndProperty>
        <EndProperty Name="ResourceType">
          <ScalarProperty Name="ID" ColumnName="Child_ID" /></EndProperty></AssociationSetMapping>
      <AssociationSetMapping Name="FK_Parent" TypeName="MyModel.FK_Parent" StoreEntitySet="ResourceTypeHierarchy">
        <EndProperty Name="ResourceTypeHierarchy">
          <ScalarProperty Name="Child_ID" ColumnName="Child_ID" />
          <ScalarProperty Name="Parent_ID" ColumnName="Parent_ID" /></EndProperty>
        <EndProperty Name="ResourceType">
          <ScalarProperty Name="ID" ColumnName="Parent_ID" /></EndProperty></AssociationSetMapping></EntityContainerMapping>
  </Mapping>
</edmx:Mappings>

【问题讨论】:

【参考方案1】:

我相信这是按预期工作的。我的意思是,如果您将一个属性放在建立两个实体之间的关系的表上,那么该表必须(至少从数据表示理论)被表示为一个适当的实体。如果你仔细考虑一下,你就会明白为什么。您提到的数量是关系的属性,而不是任何相关实体的属性。因此,该表必须被视为实体而不是关系。

现在关于如何克服这个问题,我想到的一件事(尽管我不确定这是否会完全解决您的问题)是按照您最初的想法处理关系(没有数量)并有另一个表(将映射到模型中的实体)存储特定关系的数量。我认为这个表甚至可以对你的数据库有一个外键约束到原始关系表,虽然这个外键不能映射到你的模型上的关系(因为你没有端点的实体),但这仍然允许您在存储中保持数据完整性。

希望这会有所帮助, 维托

【讨论】:

以上是关于实体框架自引用分层多对多的主要内容,如果未能解决你的问题,请参考以下文章

学说:在自引用中删除实体(多对多)

从教义中的多对多自引用实体中获取孩子

多对多和自我参考表!获取用户拥有的所有角色。实体框架

实体框架代码优先多对多不使用 GUID 加载相关实体

教义2多对多自我引用与中间细节

多对多自引用关系