我正在使用C#和Entity Framework,先进行代码移植。
我有一个类user
,该类与自身具有多对多的关系,并具有属性Friends
。目前,我有这段代码负责创建数据库结构和关系本身。
[InverseProperty("ReverseFriends")]
public virtual ICollection<User> Friends { get; set; }
[InverseProperty("Friends")]
public virtual ICollection<User> ReverseFriends { get; set; }
但是我怀疑/希望有一种方法可以仅使用一个导航属性,而不是两个。是这样吗还是我在单一表格上进行多对多的方法?
答案 0 :(得分:1)
您的模型和数据注释都很好。
但是我怀疑/希望有一种方法可以仅使用一个导航属性,而不是两个。
阴性。关系始终包含两个实体(尽管它们可以是一个实体,并且与此处的类型相同)。而且许多不带流利的API都需要两个导航属性。
想象两个实体A
和B
之间的多对多关系:
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"));
这允许您获取用户为UserId
或FriendId
的所有联接表元素,即所有用户朋友:
var query = db.Users.Select(u => new
{
User = u,
AllFriends = u.Friends.Concat(u.FriendOf).ToList(),
});