2012-05-04 75 views
10

錯誤:未處理的異常4.3.1

未處理的異常信息:System.Data.SqlClient.SqlException:操作失敗,因爲索引或統計名爲「IX_ID」表上已經存在「 PrivateMakeUpLessons'。

模型(簡體,建立在調試一個單獨的測試項目):

public abstract class Lesson 
{ 
    public Guid ID { get; set; } 
    public string Room { get; set; } 
    public TimeSpan Time { get; set; } 
    public int Duration { get; set; } 
} 

public abstract class RecurringLesson : Lesson 
{ 
    public int DayOfWeek { get; set; } 
    public DateTime StartDate { get; set; } 
    public DateTime EndDate { get; set; } 
    public string Frequency { get; set; } 
} 

public class PrivateLesson : RecurringLesson 
{ 
    public string Student { get; set; } 
    public string Teacher { get; set; } 
    public virtual ICollection<Cancellation> Cancellations { get; set; } 
} 

public class Cancellation 
{ 
    public Guid ID { get; set; } 
    public DateTime Date { get; set; } 
    public virtual PrivateLesson Lesson { get; set; } 
    public virtual MakeUpLesson MakeUpLesson { get; set; } 
} 

public class MakeUpLesson : Lesson 
{ 
    public DateTime Date { get; set; } 
    public string Teacher { get; set; } 
    public virtual Cancellation Cancellation { get; set; } 
} 

配置:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Lesson>().ToTable("Lessons"); 
    modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons"); 
    modelBuilder.Entity<PrivateLesson>().ToTable("PrivateLessons"); 
    modelBuilder.Entity<MakeUpLesson>().ToTable("PrivateMakeUpLessons"); 

    modelBuilder.Entity<Cancellation>() 
     .HasOptional(x => x.MakeUpLesson) 
     .WithRequired(x => x.Cancellation); 

    base.OnModelCreating(modelBuilder); 
} 

注意

這工作得很好EF 4.2。我的模型有問題嗎?實際的模型要複雜得多,這就是爲什麼我將所有類都抽象出來的原因。另外,我正在處理現有的數據庫,所以我需要使用Table-Per-Type繼承。

如果我將CancellationPrivateMakeUpLesson的關係從1改變到0..1到0..1到0..1,它就起作用。這是不可取的,因爲如果沒有Cancellation,就不能有PrivateMakeUpLesson

此外,如果我讓PrivateMakeUpLesson不從Lesson繼承,那麼它也可以工作,但它是一個教訓,需要保留現有的業務邏輯。

我很感激任何指導。謝謝!

編輯

開始賞金。我無法找到有關EF 4.2和EF 4.3之間在代碼優先的索引生成方面發生了什麼變化的任何文檔。很明顯,EF 4.3創建了更多的索引,並且命名方案已經改變,但我想知道EF中是否存在錯誤,或者我的模型或流利API配置是否存在根本性錯誤。

+2

你的錯誤提到你正在使用現有的數據庫,但是這個錯誤看起來更像是EF試圖創建和索引 - 這隻有在EF修改數據庫模式時纔會發生。你使用EF遷移? –

+0

@LadislavMrnka不,我仍然手動編寫遷移腳本。我剛纔提到我已經在生產中有一個數據庫,所以我不想更改繼承映射方法。 –

+0

我同意這聽起來像它嘗試重新創建數據庫,因爲您正在使用自定義遷移腳本,您是否禁用了數據庫的重新創建約定? –

回答

9

由於EF 4.3中,索引數據庫的創建過程中添加freign鍵列。有一個錯誤會導致索引不止一次被創建。這將在未來的EF版本中修復。

在此之前,您可以通過使用Migrations而不是數據庫初始值設定項(或Database.Create()方法)創建數據庫來解決此問題。

生成初始遷移後,您需要刪除多餘的呼叫Index()

CreateTable(
    "dbo.PrivateMakeUpLessons", 
    c => new 
     { 
      ID = c.Guid(nullable: false), 
      ... 
     }) 
    .PrimaryKey(t => t.ID) 
    .ForeignKey("dbo.Lessons", t => t.ID) 
    .ForeignKey("dbo.Cancellations", t => t.ID) 
    .Index(t => t.ID) 
    .Index(t => t.ID); // <-- Remove this 

繼續在運行時創建的數據庫,你可以使用MigrateDatabaseToLatestVersion初始化。

+0

感謝您的解決方法!這個bug會在EF 5.0中解決嗎?或更高版本? – Slauma

+0

感謝您花時間查看此內容併發布解決方法。我甚至沒有看過EF遷移,所以我想這是一個很好的理由,需要一些時間來檢查它。 –

+0

@Slauma,目前它看起來像這樣會在EF 5.0.0的RTM版本中修復。 – bricelam

2

我在代碼中得到了一個非常類似的錯誤。嘗試將取消列表放入課程類中。這就是解決我的問題。

+0

不幸的是,這不是我的解決方案,因爲對於不同類型的課程實際上有不同類型的取消。爲了便於理解,我在此簡化了模型。 –

2

下面我描述了兩種情況,可能會出錯。請點擊我提供的鏈接以深入瞭解我的解釋。


首先
LessonRecurringLessonabstract類(所以你想擁有它作爲基類)。
您正在創建LessonRecurringLesson實體的表,這將導致Table per hierarchy structure簡要說明
創建類的基表將導致包含所有繼承表的列一個大表。所以PrivateLesson,MakeUpLesson和其他所有繼承實體的所有屬性將被存儲在Lessons表中。 EF還將添加一個Discriminator列。此列的值默認爲持久類名稱(如「PrivateLesson」或「MakeUpLesson」),只有匹配該特定實體的列(與Discriminator值匹配)纔會用於該特定行。


您也映射繼承類,如PrivateLessonMakeUpLesson。這將強制EF使用Table per Type structure,這會導致每個類有一個表。這可能會導致你現在面臨的衝突。



你的例子顯示了你有一個一對一的關係(Cancellation -> MakeUpLesson)和一個一對多的關係(Cancellation -> PrivateLesson),因爲PrivateLessonMakeUpLesson都(間接)從Lesson繼承結合第一個描述的方案可能會導致問題,因爲它會導致數據庫中每個實體有2個外鍵關係。 (一個使用表格每層結構和一個使用表格每種類型結構)。

另外this post可以幫助您定義正確的一對一定義。


通過執行以下步驟請驗證:
我假設你有你自己的測試環境,以便您可以創建新的測試數據庫

被刪除到Cancellation的關係評論此房型的所有房產:

public class PrivateLesson : RecurringLesson 
{ 
    public string Student { get; set; } 
    public string Teacher { get; set; } 
    //public virtual ICollection<Cancellation> Cancellations { get; set; } 
} 

public class Cancellation 
{ 
    public Guid ID { get; set; } 
    public DateTime Date { get; set; } 
    //public virtual PrivateLesson Lesson { get; set; } 
    //public virtual MakeUpLesson MakeUpLesson { get; set; } 
} 

public class MakeUpLesson : Lesson 
{ 
    public DateTime Date { get; set; } 
    public string Teacher { get; set; } 
    //public virtual Cancellation Cancellation { get; set; } 
} 

並取消配置它:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Lesson>().ToTable("Lessons"); 
    modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons"); 
    modelBuilder.Entity<PrivateLesson>().ToTable("PrivateLessons"); 
    modelBuilder.Entity<MakeUpLesson>().ToTable("PrivateMakeUpLessons"); 

    //modelBuilder.Entity<Cancellation>() 
    // .HasOptional(x => x.MakeUpLesson) 
    // .WithRequired(x => x.Cancellation); 

    base.OnModelCreating(modelBuilder); 
} 

2. 創建一個新的空數據庫
讓EF生成此空數據庫爲您的表結構。
4. 驗證第一種情況。如果這是真的,則需要首先通過使用Table per hierarchy structureTable per Type structure來解決。可能你想使用Table per hierarchy structure,因爲(如果我理解你的問題)已經有一個生產環境。

+0

「這將強制EF使用Table per Type結構,每個類都會生成一個表,這可能會導致您現在面臨的衝突。」 - 我不明白如何使用TPT導致問題。我在我的問題中提到,這是我想要使用的繼承策略。它在EF 4.2中正常工作,所以我不明白爲什麼在我的域模型中使用TPT是一個糟糕的選擇。 –

+0

TPT不是一個糟糕的選擇,但如果EF同時使用兩種結構,您將會發生衝突。例如,如果你有'班級:基地'。 如果EF使用TPH,您將得到一個表[Base],其中包含Base和Side的所有列。如果您同時擁有TPT結構,那麼您還有** [列表]中列出的所有列。我更喜歡你已經使用過的TPT。但EF支持在數據庫中保存被繼承實體的兩種方法 – hwcverwe

5

在我看來,這顯然是一個錯誤。

問題始於觀察到EF完全創建索引IX_ID。如果您將模型剝離到以下...

public abstract class Lesson 
{ 
    public Guid ID { get; set; } 
} 

public class RecurringLesson : Lesson 
{ 
} 

public class MyContext : DbContext 
{ 
    public DbSet<Lesson> Lessons { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons"); 
    } 
} 

...讓EF創建數據庫模式,你會得到兩個表LessonsRecurringLessons人們所期待的TPT繼承映射。但我不知道爲什麼它會創建指數表RecurringLessons

  • 指數PK_RecurringLessons(集羣,獨特的)和索引列ID
  • 指數IX_ID(不聚集,不唯一)與索引列ID再次

我不知道是否有任何好處的數據庫在同一列上有第二個索引。但我的理解是沒有意義1)建立在已經覆蓋的PK聚集索引同一列的指數,以及2)創建一個列是主一個不是唯一的指數關鍵,因此必然是唯一的。

而且由於一對一的關係EF試圖對因此關聯是PrivateMakeUpLessons的的表中創建索引。 (這是相關的(而不是本金),因爲Cancellation需要在實體MakeUpLesson

ID是在同一時間在該協會的外鍵(和主鍵,因爲一到一個關係總是實體框架中的共享主鍵關聯)。 EF顯然總是在關係的外鍵上創建索引。但對於一對多關係,這不是問題,因爲FK列與PK列不同。不那麼對於一個對一relatonships:該FK和PK是相同的(即ID),因此EF試圖創建一個索引IX_ID對於已經存在由於TPT繼承映射該一對一的關係(其從數據庫的角度來看也會導致一對一的關係)。

與此相同的考慮適用於此處:表PrivateMakeUpLessons在列ID上具有羣集PK索引。爲什麼要求同一列上的第二個索引IX_ID

除EF似乎並沒有檢查它已經要創建一個名稱IX_ID的指數爲TPT繼承,最終導致數據庫異常時,DDL發送到創建數據庫模式。

EF 4.2(之前)沒有建立在所有的任何指標(除PK指數),這是在EF 4.3推出,尤其是指數是FK列。

我沒有找到一個解決辦法。在最壞的情況下,您必須手動創建數據庫模式,並避免EF嘗試創建它(=禁用數據庫初始化)。在最好的情況下,有一種方法可以禁用自動創建FK索引,但我不知道這是否可能。

你可以在這裏提交錯誤報告:從EF開發團隊http://connect.microsoft.com/VisualStudio

或者,也許有人會在這裏看到你的問題,並提供解決方案。

+0

感謝您一直在尋找這一點。我在查看EF 4.2和4.3中生成的索引時得出了同樣的結論,但我不確定自己是否做錯了什麼或者是否有解決方法。我會報告這個錯誤,看看ADO.NET團隊中的某個人是否可以跟進這個問題。再次感謝! –

1

當我的項目是從EF 6.0.2更新至6.1.1 EF,我有這樣的問題,然後再返回到6.0.2,舊版本的迴歸後,消失