Entity Framework 多对多相同 table

Entity Framework many to many on same table

我正在使用 C# 和 Entity Framework,代码优先迁移。

我有一个class,user,它与自身有多对多关系,与属性 Friends。目前我有这段代码负责创建数据库结构和关系本身。

[InverseProperty("ReverseFriends")]
public virtual ICollection<User> Friends { get; set; }
[InverseProperty("Friends")]
public virtual ICollection<User> ReverseFriends { get; set; }

但是我 suspect/hope 有一种方法可以只用一个导航 属性,而不是两个。是这样吗?或者我这样做的方式是在单个 table 上做多对多的方式吗?

你的模型和数据注释都很好。

However I suspect/hope there is a way to do this with only a single navigation property, instead of two.

否定。关系总是包括两个 实体(尽管它们可以是一个并且与这里的类型相同)。多对多 w/o 流畅 API 需要 两个 导航属性。

想象一下两个实体 AB 之间的多对多关系:

public class A
{
    [InverseProperty("As")]
    public ICollection<B> Bs { get; set; }
}

public class B
{
    [InverseProperty("Bs")]
    public ICollection<A> As { get; set; }
}

请注意,在这种情况下,InverseProperty 注释是可选的,因为 EF 可以自动关联两个导航属性并形成单一的多对多关系。

现在替换A == B。我们得到的是:

public partial class A
{
    [InverseProperty("As")]
    public ICollection<A> Bs { get; set; }
}

public partial class A
{
    [InverseProperty("Bs")]
    public ICollection<A> As { get; set; }
}

或合并后:

public class A
{
    [InverseProperty("As")]
    public ICollection<A> Bs { get; set; }
    [InverseProperty("Bs")]
    public ICollection<A> As { get; set; }
}

即正是你所做的。

如果您只想通过一个导航创建关系 属性,那么您必须使用流畅 API。例如,如果您只想保留 Friends,例如

public class User
{
    // ...
    public virtual ICollection<User> Friends { get; set; }
}

您需要以下流畅的配置:

modelBuilder.Entity<User>()
   .HasMany(u => u.Friends)
   .WithMany(); // no inverse navigation property

但请注意,虽然这允许您定义关系,但 Friends 属性 仍将仅包含连接 table 中的元素,假设 UserId等于 User.Id。它不会包含连接 table 的元素,其中 FriendId 等于 User.Id.

最好是保留两个导航属性并显式配置连接 table,例如

public class User
{
    // ...
    public virtual ICollection<User> Friends { get; set; }
    public virtual ICollection<User> FriendOf { get; set; }
}

modelBuilder.Entity<User>()
   .HasMany(u => u.Friends)
   .WithMany(u => u.FriendOf);
   .Map(m => m.ToTable("UserFriends")
       .MapLeftKey("UserId")
       .MapRightKey("FriendId"));

这允许您获取用户为 UserIdFriendId 的所有加入 table 元素,即所有用户好友:

var query = db.Users.Select(u => new
{
    User = u,
    AllFriends = u.Friends.Concat(u.FriendOf).ToList(),
});