使用Table-per-Hierarchy(实体框架4)映射片段异常

时间:2011-01-13 00:44:17

标签: sql-server entity-framework database-design entity-framework-4 table-per-hierarchy

如果我有这样的SQL Server表:

Location
----------
LocationId int PK 
Field1 int
Field2 int
etc

Score
------------------------------------
LocationId int PK, FK
IsSingleLevel bit PK (discriminator)
Field1 int
Field2 int
etc

从技术上讲,这被映射为位置1..0 .. *分数,但是使用LocationId / IsSingleLevel的PK,它是一个位置1..0..2。

当我在EDMX上拖动它时,按预期设置所有内容(抽象实体,从基础实体中移除鉴别器等)。

EF给出了这个错误三次(一个用于基本实体,一个用于两个派生实体):

  

错误6错误3025:从第2146行开始映射片段时出现问题:必须指定表LocationScore的所有关键属性(LocationScore.LocationId,LocationScore.IsSingleLevelScore)的映射。

我按照示例here

发生错误是因为我将鉴别器作为数据库中PK的一部分,并且鉴别器未映射到模型上,因此我得到了错误。

我不能将LocationId作为PK中的唯一字段,因为那时一个位置只能有1个分数,我需要它有两个分数(一个单一级别,一个整体)。

最终结果是我希望能够做到这一点:

Locations.Include("LocationOverallScore").Single();
Locations.Include("LocationSingleLevelScore").Single();

LocationOverallScoreLocationSingleLevelScore是来自LocationScore基础(抽象实体)的派生实体。

TPH不是正确的设计吗?我的数据库设计错了吗?目的是我不希望有不同分数的2个物理表 - 因为表格很大,我不想重复列。

我可以想到两种可能的解决方法:

1 - 制作一个视图(LocationScore),其中UNION是两个表(因此每个LocationId会返回2行) - 但我仍然认为我不能“TPH”这个。我不想手动执行JOIN,我想急切加载。

2 - 将IDENTITY列添加到Score,这可以是PK。

你们能想到另一种解决方案吗?

1 个答案:

答案 0 :(得分:1)

我找到了解决方案。我不是百分之百满意,但它确实满足了我没有两张桌子的要求。

在数据库方面:

<强> LocationScore:

  • LocationScoreId:IDENTITY,PK
  • LocationId:FK
  • LocationId / IsSingleLevelScore的唯一索引

我将它导入我的EDMX - 并包含了LocationId FK(我从来没有这样做 - 但在这个例子中它是必需的)。

创建派生实体,映射字段,设置鉴别器。

在Location - &gt;之间创建关联LocationOverallScore,位置 - &gt; LocationScore(基于LocationId参考约束)。

一切正常。

一个缺点是是因为LocationId不是LocationScore表中PK的一部分,它在Location和LocationOverallScore之间是1 .. *,实际上它应该只是1 .. 1。

我通过hook属性在模型中强制执行此业务需求:

public class Location
{
   // EF navigational properties - required
   public ICollection<LocationOverallScore> LocationOverallScores { get; set; }
   public ICollection<LocationSingleLevelScore> LocationSingleLevelScores { get; set; }

   // Hook properties
   public LocationOverallScore OverallScore
   {
      get { return LocationOverallScores.SingleOrDefault(); }
   }
   public LocationSingleLevelScores SingleLevelScore
   {
      get { return LocationSingleLevelScores .SingleOrDefault(); }
   }
}

因此,如果有多条记录,.SingleOrDefault()将抛出异常 - 由于唯一索引,它将永远不会出现。

所以我现在可以这样做:

var location = ctx.Locations.Include("LocationOverallScores").Single();
var overallScore = location.OverallScore;

这就是我现在要做的事情。