自引用多对多关系

Ice*_*urn 6 entity-framework-core asp.net-core

我有一个Ticket实体:

    public class Ticket
    { 
        public int Id { get; set; }
        public string Title { get; set; }

        public virtual ICollection<Relation> RelatedTickets { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

我想在Entity Framework Core中设置多对多自相关,因此我建立了两个一对多关系:

public class Relation
{
    [Required, ForeignKey("TicketFrom")]
    public int FromId { get; set; }

    [Required, ForeignKey("TicketTo")]
    public int ToId { get; set; }

    public virtual Ticket TicketFrom { get; set; }
    public virtual Ticket TicketTo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用流畅的API创建关系:

        builder.Entity<Relation>()
               .HasKey(uc => new { uc.FromId, uc.ToId });
        builder.Entity<Relation>()
           .HasOne(c => c.TicketFrom)
           .WithMany(p => p.RelatedTickets)
           .HasForeignKey(pc => pc.FromId);
        builder.Entity<Relation>()
           .HasOne(c => c.TicketTo)
           .WithMany(p => p.RelatedTickets)
           .HasForeignKey(pc => pc.ToId);
Run Code Online (Sandbox Code Playgroud)

但结果我有一个错误:

无法在“ Ticket.RelatedTickets”和“ Relation.TicketTo”之间创建关系,因为“ Ticket.RelatedTickets”和“ Relation.TicketForm”之间已经存在关系。导航属性只能参与单个关系。

可能的解决方案是直接将Parent关系添加到TicketEntity

public class Ticket
{ 
    public int Id { get; set; }

    [Required, ForeignKey("ParentRelation")]
    public Nullable<int> ParentRelationId { get; set; }

    public virtual Ticket ParentRelation {get;set;}

    public virtual ICollection<Ticket> RelatedTickets { get; set; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

使用像这样的流利的api:

modelBuilder.Entity<Ticket> =>
{
    entity
        .HasMany(e => e.RelatedTickets)
        .WithOne(e => e.ParentRelation) 
        .HasForeignKey(e => e.ParentRelationId );
});
Run Code Online (Sandbox Code Playgroud)

但是这样存储父级关系看起来“肮脏”。
什么是正确的方法?

Iva*_*oev 6

不可能只有一个具有关系的集合。您需要两个,一个与票证相等的TicketFrom关系,第二个与票证相等的关系TicketTo

像这样:

模型:

public class Ticket
{ 
    public int Id { get; set; }
    public string Title { get; set; }

    public virtual ICollection<Relation> RelatedTo { get; set; }
    public virtual ICollection<Relation> RelatedFrom { get; set; }
}

public class Relation
{
    public int FromId { get; set; }
    public int ToId { get; set; }

    public virtual Ticket TicketFrom { get; set; }
    public virtual Ticket TicketTo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

组态:

modelBuilder.Entity<Relation>()
    .HasKey(e => new { e.FromId, e.ToId });

modelBuilder.Entity<Relation>()
    .HasOne(e => e.TicketFrom)
    .WithMany(e => e.RelatedTo)
    .HasForeignKey(e => e.FromId);

modelBuilder.Entity<Relation>()
    .HasOne(e => e.TicketTo)
    .WithMany(e => e.RelatedFrom)
    .HasForeignKey(e => e.ToId);
Run Code Online (Sandbox Code Playgroud)

请注意,使用Parent的解决方案并不等效,因为它会创建one-to-many关联,而如果我理解正确,那么您正在寻找many-to-many


Здр*_*ков 5

这里很好地解释了如何在 EF Core 多对多自引用关系中建立多对多关系

每个集合或引用导航属性只能是单个关系的一部分。而具有显式连接实体的多对多关系是通过两个一对多关系实现的。连接实体包含两个引用导航属性,但主实体只有一个集合导航属性,必须与其中之一相关联,但不能同时与两者相关联。

   builder.Entity<Relation>()
           .HasKey(uc => new { uc.FromId, uc.ToId });

    builder.Entity<Relation>()
       .HasOne(c => c.TicketFrom)
       .WithMany() // <-- one of this must be empty
       .HasForeignKey(pc => pc.FromId)
       .OnDelete(DeleteBehavior.Restrict);

    builder.Entity<Relation>()
       .HasOne(c => c.TicketTo)
       .WithMany(p => p.RelatedTickets)
       .HasForeignKey(pc => pc.ToId);
Run Code Online (Sandbox Code Playgroud)

只需确保 WithMany 与相应导航属性的存在/不存在完全匹配。

请注意,您必须关闭删除级联。