【问题标题】:Creating a double linked list in Entity Framework在实体框架中创建双链表
【发布时间】:2014-06-04 09:36:57
【问题描述】:

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

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。有什么方法可以实现吗?

【问题讨论】:

    标签: entity-framework entity-framework-6


    【解决方案1】:

    我已经通过使用 fluent api 方法实现了您想要的。我需要从 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 实体分配给另一个的 Previous 属性,则分配的实体将在 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
    }
    

    【讨论】:

    • 看来您必须同时更新 Next 和 Previous 才能正常工作。仅设置 Next 不会更新 Next 的 Previous。此外,将 Next 设置为 null 不会将 Next 的 Previous 设置为 null。
    • 不——你错了。将 Foo 的所有属性设为虚拟,您会发现不需要刷新。当一个变化时,它会自动更新相关实体的值。所以实际上第二次使用是没有必要的。我试过了。
    • 如果 Foo 的 1 个属性是非虚拟的,则需要刷新才能看到结果。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2017-04-03
      • 2012-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多