Entity Framework 建模一对多关系

Entity Framework modelling one-to-many relationship

我不知道如何在 EF 中配置以下关系:

假设我需要创建一些语言词典模型。我有一个项目(例如单词)用一种语言和另一种语言。这两项之间存在某种关系。

例如:"hund"(德语)-> "dog"(英语),关系类型为"Translate"

public enum Language
{
    English,
    German,
}

public class Item
{
    public long ID { get; set; }
    [Required]
    public string Value { get; set; }
    [Required]
    public Language Language { get; set; }

    public virtual ICollection<ItemRelation> ItemRelations { get; set; }
}

public enum ItemRelationType
{
    Translate,
    Synonym,
}

public class ItemRelation
{
    public long ID { get; set; }

    [ForeignKey("ItemID")]
    public Item Item { get; set; }

    [ForeignKey("RelativeItemID")]
    public Item RelativeItem { get; set; }

    [Required]
    public ItemRelationType Type { get; set; }
}

EF 标准迁移在一种情况下抛出一些错误,或者在另一种情况下创建我不会使用的列或 FK(Item_ID 等)。

我想我需要配置一些流畅的 api - 但我不确定如何...

我想你或许可以摆脱这个:

public class ItemRelation
{

    public long Id { get; set; }

    [ForeignKey("PrimaryItemId")]
    public Item Item { get; set; }
    public long PrimaryItemId { get; set; }

    [ForeignKey("RelatedItemId")]
    public Item RelatedItem { get; set; }
    public long RelatedItemId { get; set; }

    public ItemRelationType RelationType;
}

请注意,此 class 现在与 Item 实体有两个关系,从而产生两个外键。请注意,每个 Item 属性 都有一个 [ForeignKey] 属性,字符串参数指定 long 用作外键列。

将此答案视为对不同轨道的推动。进一步研究这个主题,看看它是否适合您的用例。

您缺少实际的 FK 字段 ItemIDRelativeItemID:

要配置导航 属性,您可以使用 InverseProperty 属性,同时禁用一些 EF 约定(如下所示)。

public class ItemRelation
{
   public long ID { get; set; }

   public long ItemID { get; set; } // Missing 
   [ForeignKey("ItemID")]
   [InverseProperty("ItemRelations")]
   public virtual Item Item { get; set; }

   public long RelativeItemID { get; set; } // Missing 
   [ForeignKey("RelativeItemID")]
   [InverseProperty("RelativeItemRelations")]
   public virtual Item RelativeItem { get; set; }

   [Required]
   public ItemRelationType Type { get; set; }
}

上面的声明使用 virtual 以便延迟加载工作。这不是必需的,您可以将其删除。结果是懒加载不行,这也可以。

假设您需要为第二个关系添加导航 属性,您需要添加 属性:

public class Item
{
...
   public virtual ICollection<ItemRelation> RelativeItemRelations { get; set; }
...
}

然后通过覆盖 OnModelCreating 禁用级联删除约定,如果您还没有这样做,在您的上下文 class 中如下所示:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{
...
   modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
...

此解决方案应该有效,但它实际上禁用了对所有一对多关系的级联删除。好处是你可以通过使用流利的 api.

根据具体情况取回它

实现你想要的第二种方法是使用 fluent api 如下:

将第二个导航 属性 添加到您的 Item 实体:

public class Item
{
...
   public virtual ICollection<ItemRelation> RelativeItemRelations { get; set; }
...
}

ItemRelation 实体缺少 FK,所以这里是:

public class ItemRelation { public 长 ID { get;放; }

   public long ItemID { get; set; } // Missing 
   public virtual Item Item { get; set; }

   public long RelativeItemID { get; set; } // Missing 
   public virtual Item RelativeItem { get; set; }

   [Required]
   public ItemRelationType Type { get; set; }
}

并且要配置导航 属性,并避免级联问题,您只需使用 fluent api:

定义关系
public TheContext : DbContext
{
   public DbSet<Item> Items { get; set; }
   public DbSet<ItemRelation> ItemRelations { get; set; }

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
      base.OnModelCreating(modelBuilder);

      modelBuilder.Entity<ItemRelation>()
                .HasRequired(e => e.Item)
                .WithMany(t => t.ItemRelations)
                .HasForeignKey(e => e.ItemID)
                .WillCascadeOnDelete(false);

      modelBuilder.Entity<ItemRelation>()
                .HasRequired(e => e.RelatedItem)
                .WithMany(t => t.RelativeItemRelations)
                .HasForeignKey(e => e.RelativeItemID)
                .WillCascadeOnDelete(false);       


      // Uncomment the following if you want to disable all cascading deletes and automatic fk creation conventions
      // modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
      // modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
      // modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

    ...
  }
}

Read here for an opinion on why you might consider disabling those conventions.