2013-05-06 68 views
1

如果我有以下代碼生成我的數據庫,它將TankComponent表中的外鍵分配給Asset表而不是Tank表。有人能解釋爲什麼嗎?我是否需要關閉Fluent API中的特定約定或更改?它真的只是看着列名嗎?從繼承無法正確映射外鍵

[Table("Asset")] 
public abstract class Asset 
{ 
    [Key] 
    public int AssetId { get; set; } 

    public string Name { get; set; } 

    public string Description { get; set; } 
} 


[Table("Tank")] 
public class Tank : Asset 
{ 
    public Tank() 
    { 
     this.TankCompnents = new Collection<TankComponent>(); 
    } 

    public int TankField1 { get; set; } 

    public ICollection<TankComponent> TankCompnents { get; set; } 

    [NotMapped] 
    public IEnumerable<Floor> Floors { get { return this.TankCompnents.OfType<Floor>(); } } 
} 


[Table("TankComponent")] 
public abstract class TankComponent 
{ 
    [Key] 
    public int TankComponentId { get; set; } 

    [ForeignKey("Tank")] 
    public int AssetId { get; set; } 
    public Tank Tank { get; set; } 

    public string Name { get; set; } 
} 

//forgot this in initial post 
protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 

    modelBuilder.Entity<Tank>() 
    .Map(m => 
    { 
     m.Properties(a => new { a.AssetId, a.Name, a.Description }); 
     m.Requires("AssetType").HasValue(1); 
     m.ToTable("Asset"); 
    }) 
    .Map(m => 
     { 
      m.Properties(t => new { t.AssetId, t.TankField1 }); 
      m.ToTable("Tank"); 
     }); 
} 
+0

我無法用EF 5.0,.NET 4.0,SQL Express重現此操作。我從'TankComponent'獲得FK到'Tank'(我必須添加一個派生自'TankComponent'的具體類來使模型有效,我猜你也有這樣的類。)。你使用什麼版本? – Slauma 2013-05-06 17:55:43

+0

我忘了添加我的Fluent API代碼到這個: protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity () .MAP(M => { m.Properties(A =>新的{a.AssetId,a.Name,a.Description}); m.Requires( 「AssetType」) 。的HasValue(1); m.ToTable(「Asset」); } ) .MAP(M => { m.Properties(T =>新的{t.AssetId,t.TankField1}); m.ToTable( 「罐」); }); } – stumpykilo 2013-05-06 18:21:30

+0

是的,Slauma。我有一個從TankComponent派生的具體類。我使用EF 6.0,因爲我認爲它可以解決這個問題。在5.0 – stumpykilo 2013-05-06 18:23:36

回答

0

這映射行...

m.Requires("AssetType").HasValue(1); 

...和你的評論似乎表明,你可能有一個誤區表每類型(TPT)繼承是如何工作的。

EF不需要基類Asset表中的特定列來檢測具有給定主鍵值的實體的實際類型是什麼 - 除非要使用Table-Per-Hierarchy(TPH)繼承映射(即在您的實體上沒有[Table]屬性的映射)。對於TPH來說,一個特定的列 - 鑑別器 - 實際上是區分這些類型所必需的,因爲繼承樹中所有實體的所有屬性都將存儲在一個表中。如果您不明確指定鑑別符 - 例如AssetType - EF將默認創建一個名爲Discriminator的列。

現在,TPT是一個不同的故事。如果查詢有其他來源的實體的實體 - 例如...

var asset = context.Assets.First(); 

... EF不僅將獨自而是一個創造的基表的SQL查詢像SELECT TOP(1) * FROM ASSETS - 可能非常複雜 - 查詢許多LEFT OUTER JOIN s屬於所有可能派生實體的許多其他表。該查詢將在Tank表中找到一行或不查找。如果它確實發現一個EF將實現Tank對象。如果不是,它將實現Asset。 (不能在這裏是如此,因爲Assetabstract但假設此時它會abstract。)如果Asset具有其他派生類型EF將加入他們的桌子,以及與有關根據存在的具體實體類型再決定加入行。

因此,使用TPT類型不是由特殊列檢測,而是僅由(左外部)表連接的結果檢測到。

上面的行似乎混淆了EF。但它確實不屬於TPT映射,我將使用Fluent API刪除整個映射。

我測試過,當你刪除映射的結果是正確的 - 即FK關係TankComponentTank表(未Asset表)之間產生。

+0

感謝您的解釋!我非常喜歡EF代碼優先,特別是TPH和TPT概念。我非常感謝您的意見。當你說你測試了結果,你是否得到了在你的數據庫中創建的表(包括資產表)? – stumpykilo 2013-05-06 20:29:28

+0

@stumpykilo:是的,基礎屬性的'Asset'表和派生屬性的'Tank'表。 – Slauma 2013-05-06 20:51:55