在Entity Framework中创建双链表

时间:2014-06-04 09:36:57

标签: entity-framework entity-framework-6

我有一个对象,可以选择引用下一个和/或前一个记录。像这样:

public class Foo
{
  [Key]
  public int Id {get; set;}

  [ForeignKey("Previous")]
  public int? PreviousId {get; set;}

  public Foo Previous {get; set;}

  [InverseProperty("Previous")]
  public Foo Next {get; set;}
}

不幸的是,这不起作用,而是导致错误消息Unable to determine the principal end of an association between the types Foo and Foo

我们的想法是,通过设置PreviousId,上一个Foo将自动由EF设置Next。这是为了防止NextPrevious导致错误导致的错误。另请注意,PreviousId可以是null,在这种情况下,数据库中的任何记录都不应该有Next指向该记录。有没有办法实现这个?

2 个答案:

答案 0 :(得分:7)

我通过流畅的api aproach成功实现了你想要的目标。我需要从Foo类中删除PreiousId属性 - 稍后将通过映射添加它。

public class Foo
{
    [Key]
    public virtual int Id { get; set; }

    public virtual Foo Previous { get; set; }

    public virtual Foo Next { get; set; }
}

将所有属性更改为虚拟,因为这将允许ef动态跟踪内存中属性的状态。然后在DbContext派生类中,你需要覆盖OnModelCreating方法并在那里定义映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Foo>()
        .HasOptional(f => f.Next)
        .WithOptionalPrincipal(f => f.Previous)
        .Map(c => c.MapKey("PreviousId"));

    base.OnModelCreating(modelBuilder);
}

这将添加到Foo表的PreviousId列,它将成为关系的外键。它将定义1-0的关系。如果将一个Foo实体分配给另一个属性,则分配的实体将在Next属性中引用它。我用以下代码测试了它:

using(MyDbContext context = new MyDbContext("Test"))
{
    context.Database.Delete();
    Foo foo1 = context.Foos.Create();
    Foo foo2 = context.Foos.Create();
    foo1.Next = foo2;
    context.Foos.Add(foo1);
    context.Foos.Add(foo2);
    context.SaveChanges();
}
using (MyDbContext context = new MyDbContext("Test"))
{
    Foo foo1 = context.Foos.OrderBy(f => f.Id).First();
    Foo foo2 = context.Foos.OrderBy(f => f.Id).Skip(1).First();
    // foo1.Next == foo2 and foo2.Previous == foo1
}

答案 1 :(得分:1)

对于那些使用实体框架核心的人来说,这就是我最后做的事情

public class LinkedListNode
{
    public int Id { get; set; }
    public int? NextId { get; set; }
    public virtual LinkedListNode Next { get; set; }
    public int? PrevId { get; set; }
    public virtual LinkedListNode Prev { get; set; }
    public long SortOrder { get; set; }
}


protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<LinkedListNode>()
        .HasOne<LinkedListNode>(x => x.Next)
        .WithMany()
        .HasPrincipalKey("Id")
        .HasForeignKey("NextId")
        .OnDelete(DeleteBehavior.Restrict)
        .IsRequired(false);

    builder.Entity<LinkedListNode>()
        .HasOne<LinkedListNode>(x => x.Prev)
        .WithMany()
        .HasPrincipalKey("Id")
        .HasForeignKey("PrevId")
        .OnDelete(DeleteBehavior.Restrict)
        .IsRequired(false);
}