2015-09-02 128 views
1

我很想看看爲什麼實體框架在以下上下文中不允許1對1關係;1對1可選與實體框架的關係

ClassA { 
    public int ID; 
    [ForeignKey("ClassB")] 
    public int ClassBID; 
    public ClassB classB; 
} 

ClassB { 
    public int ID; 
    [ForeignKey("ClassA")] 
    public int ClassAID; 
    public ClassA classa; 
} 

即一對一關係,從而我可以導航到linq。

我的背景是我有一輛車。每輛車可以有一個設備(如果沒有的話,它是空的)。每個設備將有一個可選車輛。

如果有人能解釋他爲什麼無效(或支持),並解釋我將如何解決我的問題,我真的會讚賞它。

在此先感謝。

回答

0

如果我瞭解設備可以有零個或一個車輛,反之亦然。
在舊的DB模型中,兩個表(設備或車輛)中的一個應該有一個引用另一個表的可爲空的字段。
要在EF中配置它,您必須使用數據註釋或流暢的界面。 這裏的模型和上下文的代碼

public class ClassA 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public virtual ClassB ClassB { get; set; } 
} 

public class ClassB 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public virtual ClassA ClassA { get; set; } 
} 

class Context : DbContext 
{ 
    public Context(DbConnection connection) 
     : base(connection, false) 
    { } 

    public DbSet<ClassA> As { get; set; } 
    public DbSet<ClassB> Bs { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<ClassB>().HasOptional(c => c.ClassA).WithOptionalDependent(c => c.ClassB); 
    } 
} 

正如你所想象,有使用該模型的一些限制。即模型(POCO模型)允許您擁有一個classA1,引用一個引用classA2的classB1(類型相同但classA1不同的實例)。 DB和EF沒有。這裏用一個查詢爲例傾倒在EF如何在這種情況下,(我覺得很有意思!)

 using (Context context = new Context(connection)) 
     { 
      ClassA classA; 
      ClassB classB; 


      // Very simple behaviour (as expected). You can see the queries after SaveChanges() 
      classA = new ClassA {Description = "B empty"}; 
      context.As.Add(classA); 

      classA = new ClassA { Description = "B full", ClassB = new ClassB(){Description = "ClassB full"}}; 
      context.As.Add(classA); 

      classB = new ClassB { Description = "B empty"}; 
      context.Bs.Add(classB); 

      context.SaveChanges(); 
      /* 
      insert into [ClassAs]([Description]) 
       values (@p0); 

       @p0 = B full 

      insert into [ClassAs]([Description]) 
       values (@p0); 

       @p0 = B empty 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, @p1); 

       @p0 = ClassB full 
       @p1 = 1 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, null); 

       @p0 = B empty 
      */ 




      // Here a new classB references an already referenced classA. But we don't want this!!! 
      // EF works like we want, the classA is detached from the old classB then attached to the 
      // new classB. Below you can see the queries 
      classB = new ClassB { Description = "B full with the wrong A", ClassA = classA}; 
      context.Bs.Add(classB); 
      /* 
      update [ClassBs] 
       set [ClassA_Id] = null 
       where (([Id] = @p0) and ([ClassA_Id] = @p1)) 

       @p0 = 1 
       @p1 = 1 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, @p1); 

       @p0 = B full with the wrong A 
       @p1 = 1 
      */ 

      context.SaveChanges(); 
     } 

現在,最後一步...... 望着數據庫的結構這POCO模型

ClassBs(Id, Description, ClassA_Id : ClassAs) 
ClassAs(Id, Description) 

在數據庫模型中,我們可以有2個不同的具有相同ClassA實例的ClassB實例(EF不允許我們這樣做,但我們可以從SQL實現)。 使用SQL黑客後,你可以運行這個測試

 using (Context context = new Context(connection)) 
     { 
      foreach (var classB in context.Bs.ToList()) 
      { 
       if (classB.ClassA == null) 
        continue; 
       Console.WriteLine("{0} {1} {2}", classB.Id, classB.ClassA.Id, classB.ClassA.ClassB.Id); 
      } 
     } 

這個測試會引發異常

===
「System.InvalidOperationException」類型的未處理的異常出現在EntityFramework.dll

附加信息:發生關係多重性約束衝突:EntityReference中只能有一個相關對象,但該查詢返回多個相關對象。這是一個不可恢復的錯誤。

===

我們可以避免來自SQL的人做到了嗎?是的,在ClassA_Id字段上插入一個唯一約束。

+0

非常感謝您的驗證。所以在這種情況下,如果不使用流利的API,就無法解決問題了? (我問這個問題的唯一原因是因爲我喜歡嘗試將類中的所有內容都保存爲屬性,因爲我個人覺得它很容易閱讀並遵循:)) –

+0

其實我認爲不可能。 – bubi

1

您必須確定一端是非可選的(即允許1到0..1 ... 0..1到0..1不是)。一旦你這樣做,EF支持1到0 ..1通過迫使依賴方不擁有它自己的Key,而是定義相關類'Key與它的ForeignKey相同(如果你考慮它,這對於一個應該是1-to的關係是有意義的-1):

class A 
{ 
    public int Id { get; set; } 
    public int? BId { get; set; } 
    public virtual B B { get; set; } 
} 

class B 
{ 
    [Key, ForeignKey("A")] 
    public int Id { get; set; } 
    public virtual A A { get; set; } 
} 

也請注意,在細節BIdint?NULL在SQL對應Nullable<>在C#。