实体框架在同一表上多对多

时间:2018-08-12 16:35:23

标签: c# entity-framework many-to-many ef-migrations

我正在使用C#和Entity Framework,先进行代码移植。

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

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

但是我怀疑/希望有一种方法可以仅使用一个导航属性,而不是两个。是这样吗还是我在单一表格上进行多对多的方法?

1 个答案:

答案 0 :(得分:1)

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

  

但是我怀疑/希望有一种方法可以仅使用一个导航属性,而不是两个。

阴性。关系始终包含两个实体(尽管它们可以是一个实体,并且与此处的类型相同)。而且许多不带流利的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属性仍将仅包含联接表中的元素,其中UserId等于User.Id。它不会包含连接表中FriendId等于User.Id的元素。

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

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的所有联接表元素,即所有用户朋友:

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