簡體   English   中英

具有實體框架的實體之間的復雜關系

[英]Complex relationship between entities with Entity Framework

也許這是重復但我找不到這樣的話題。

我正在使用Entity Framework並在我的數據庫中有兩個表:

public class A
{
    public virtual B B1 { get; set; }
    public virtual B B2 { get; set; }
}

public class B
{
    public virtual A A1 { get; set; }
}

B1和A1或B2和A1之間沒有關系。 它就像3個單向關系。 你怎么能在Entity Framework中做到這一點?

我收到此錯誤:

保存不公開其關系的外鍵屬性的實體時發生錯誤

有誰知道如何處理這個?

提前致謝

如果A1表包含指向同一個B表的B1_Id和B2_Id,但是您希望B記錄只與A關聯一次,那么就我所知的映射方式而言,這是不可能的。 沒有什么可以阻止你在不同的A記錄中將相同的B記錄與B1或B2相關聯,那么B的A參考將如何合法地解決? 實體反映數據狀態,因此如果它是數據模式的合法/非法,則對於實體來說是相同的。 在A上有B ID會形成多對1,或者可以形成一對一,但是要在A上分享2x FK到B,你需要DB支持B的備用FK,而不是。

您可以讓A保存B中2條記錄的ID,但是B不能將單個引用映射回A,它必須偽造它。

public class A
{
   public int AId { get; set; }

   public virtual B B1 { get; set; }
   public virtual B B2 { get; set; }
}

public class B
{
   public int BId { get; set; }

   public virtual ICollection<A> RawA1 { get; private set; } = new List<A>();
   public virtual ICollection<A> RawA2 { get; private set; } = new List<A>();

   [NotMapped]
   public A A
   {
      get { return RawA1.SingleOrDefault() ?? RawA2.SingleOrDefault(); }
   }
}

需要注意的是,你不能在任何進入EF的Linq表達中使用BA,因為就EF而言,它並不知道該屬性。

替代方案是使用Ad存在於B上的1對多,然后處理限制A側的集合的允許操作。 問題是B1和B2訂單不可靠,除非B記錄上的屬性明確可確定。 A可以從集合中公開B1和B2未映射的屬性,但必須確定要為每個元素考慮哪兩個元素,或者只是公開集合。 最終,你的業務邏輯必須控制一個事實,即A應該只有2個B引用,因為數據庫不能強制執行,也不是2到1的關系,其中B可以回到A.

由於您沒有指出您正在使用的EF版本,因此請查看兩個當前版本,即EF 6.2.0和EF-core 2.2.4。

使用EF6,很容易。 映射......

modelBuilder.Entity<A>().HasRequired(a => a.B1).WithMany();
modelBuilder.Entity<A>().HasRequired(a => a.B2).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<B>().HasRequired(b => b.A1).WithMany().WillCascadeOnDelete(false);

...生成以下數據庫模型(忽略索引):

CREATE TABLE [dbo].[A] (
    [ID] [int] NOT NULL IDENTITY,
    [B1_ID] [int] NOT NULL,
    [B2_ID] [int] NOT NULL,
    CONSTRAINT [PK_dbo.A] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[B] (
    [ID] [int] NOT NULL IDENTITY,
    [A1_ID] [int] NOT NULL,
    CONSTRAINT [PK_dbo.B] PRIMARY KEY ([ID])
)

....其中帶_的字段是外鍵,其中一個可以有級聯刪除。

有了ef-core,它看起來並不那么簡單,甚至是錯誤。 第一個沖動是EF6等效:

modelBuilder.Entity<A>().HasOne(a => a.B1).WithMany();
modelBuilder.Entity<A>().HasOne(a => a.B2).WithMany();
modelBuilder.Entity<B>().HasOne(b => b.A1).WithMany();

但生成的模型並不是人們所期望的:

  CREATE TABLE [B] (
      [ID] int NOT NULL IDENTITY,
      [A1ID] int NULL,
      CONSTRAINT [PK_B] PRIMARY KEY ([ID])
  );
  CREATE TABLE [A] (
      [ID] int NOT NULL,
      [B1ID] int NULL,
      CONSTRAINT [PK_A] PRIMARY KEY ([ID]),
      CONSTRAINT [FK_A_B_B1ID] FOREIGN KEY ([B1ID]) REFERENCES [B] ([ID]) ON DELETE NO ACTION,
      CONSTRAINT [FK_A_B_ID] FOREIGN KEY ([ID]) REFERENCES [B] ([ID]) ON DELETE CASCADE
  );
  ALTER TABLE [B] ADD CONSTRAINT [FK_B_A_A1ID] FOREIGN KEY ([A1ID]) REFERENCES [A] ([ID]) ON DELETE NO ACTION;

其中一個AB關聯被解釋為1:1。 在我看來,這是一個錯誤。 WithMany指令不應留下任何解釋空間。 兩個看似相同的映射產生完全不同的數據庫關系。 這不可能是正確的。

也就是說,通過命名FK列,很容易(但不是必須)將EF放在正確的軌道上:

modelBuilder.Entity<A>().HasOne(a => a.B1).WithMany().HasForeignKey("B1_ID")
    .IsRequired();
modelBuilder.Entity<A>().HasOne(a => a.B2).WithMany().HasForeignKey("B2_ID")
    .IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<B>().HasOne(b => b.A1).WithMany().HasForeignKey("A1_ID")
    .IsRequired().OnDelete(DeleteBehavior.Restrict);

生成(忽略索引):

  CREATE TABLE [B] (
      [ID] int NOT NULL IDENTITY,
      [A1_ID] int NOT NULL,
      CONSTRAINT [PK_B] PRIMARY KEY ([ID])
  );
  CREATE TABLE [A] (
      [ID] int NOT NULL IDENTITY,
      [B1_ID] int NOT NULL,
      [B2_ID] int NOT NULL,
      CONSTRAINT [PK_A] PRIMARY KEY ([ID]),
      CONSTRAINT [FK_A_B_B1_ID] FOREIGN KEY ([B1_ID]) REFERENCES [B] ([ID]) ON DELETE CASCADE,
      CONSTRAINT [FK_A_B_B2_ID] FOREIGN KEY ([B2_ID]) REFERENCES [B] ([ID]) ON DELETE NO ACTION
  );
  ALTER TABLE [B] ADD CONSTRAINT [FK_B_A_A1_ID] FOREIGN KEY ([A1_ID]) REFERENCES [A] ([ID]) ON DELETE NO ACTION;

請注意,必須明確設置所需的外鍵字段(如果是)。 嗯,這只是一個實現細節。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM