实体框架建模一对多关系



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

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

例如:" hund"(德语) ->"狗"(英语),关系类型为"翻译"

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标准迁移在一种情况下丢了一些错误,或者创建我不在另一个情况下( Item_ID等)的列或fks。

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

您缺少实际的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禁用级联删除约定,如果您还没有,则在上下文类中,如下:

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

该解决方案应起作用,但实际上将级联删除删除到所有一对多关系中。上行是您可以使用Fluent API逐案将其恢复。

实现所需目标的第二种方法就是使用流利的API如下:

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

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

ItemRelation实体缺少FKS,因此是:

公共班级项目 { 公共长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; }
}

并配置导航属性,避免使用级联问题,您只需使用流利的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>();
    ...
  }
}

在此处阅读以获取有关您为什么可能考虑禁用这些惯例的意见。

我认为您可能可以摆脱这一点:

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;
}

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

将此答案视为不同轨道上的轻推。更多地研究此主题以查看是否适合您的用例。

最新更新