如何实现自我引用与Entity Framework Core 1.1的多对多关系?

时间:2017-06-15 16:53:28

标签: c# asp.net-core many-to-many entity-framework-core self-referencing-table

我正在按照本教程使用EF Core 1.1实现我的友谊系统:http://www.codedodle.com/2014/12/social-network-friends-database.html

Friendship.cs

public class Friendship
{
    public Guid ApplicationUserId { get; set; }
    public ApplicationUser ApplicationUser { get; set; }

    public Guid FriendId { get; set; }
    public ApplicationUser Friend { get; set; }

    public StatusCode Status { get; set; }

    public Guid ActionUserId { get; set; }
    public ApplicationUser ActionUser { get; set; }

    public byte[] Timestamp { get; set; }
}

public enum StatusCode
{
    Pending = 0,
    Accepted = 1,
    Declined = 2,
    Blocked = 3
}

ApplicationUser.cs

public class ApplicationUser : IdentityUser<Guid>
{
    ...

    public ICollection<Friendship> FriendRequestsMade { get; set; }

    public ICollection<Friendship> FriendRequestsAccepted { get; set; }

    public byte[] Timestamp { get; set; }
}

MyDbContext.cs

public class SocialCircleContext : IdentityDbContext<ApplicationUser, Role, Guid>
{

     builder.Entity<Friendship>()
        .HasIndex(x => new { x.ApplicationUserId, x.FriendId })
        .IsUnique();

     builder.Entity<Friendship>()
        .HasOne(x => x.ApplicationUser)
        .WithMany(y => y.FriendRequestsMade)
        .HasForeignKey(x => x.ApplicationUserId).OnDelete(DeleteBehavior.Restrict);

    builder.Entity<Friendship>()
        .HasOne(x => x.Friend)
        .WithMany(y => y.FriendRequestsAccepted)
        .HasForeignKey(x => x.FriendId);         
}

添加迁移初始迁移的结果

  

无法确定“ApplicationUser”类型的导航属性“Friendship.ActionUser”所代表的关系。手动配置关系,或从模型中忽略此属性。

此外,随着EF Core的快速发展,我发现了许多不同的方法。我不确定我的自我引用多对多关系的实现,有人可以给我一些建议吗?

  1. 如何定义Friendship.ActionUser和ApplicationUser之间的关系?
  2. 关于实现这种自我引用多对多关系的正确方法的任何建议? EF Core正在快速发展,我在网上找到了许多不同的方式,但它们看起来已经过时了
  3. 谢谢! :)

1 个答案:

答案 0 :(得分:1)

理想情况下,一旦关系发现中的歧义得到解决,EF应按惯例创建其余的关系,但由于错误而不会发生。 (提起Bug

您的模型类对于您要执行的操作是正确的。为了使EF成功构建模型,需要填写的缺失部分很少。

首先,让我们解决你看到的异常。 您的ApplicationUser类有2个指向Friendship的集合导航。 Friendship类有3个引用导航,指向ApplicationUser。虽然EF Core在按惯例创建关系方面做得很好,但在这种情况下,它不知道如何创建导航 - 反向导航对。因此,需要通过注释/流畅的API来输入用户。在您的情况下,您使用流畅的API创建2个关系,每侧使用2个导航。这使我们只有导航Friendship.ActionUser没有任何关系。在这一点上,EF Core对如何创建关系没有任何困惑,但由于它没有这样做的错误。这意味着您必须使用流畅的API手动配置此关系。

builder.Entity<Friendship>().HasOne(e => e.ActionUser).WithOne().HasForeignKey<Friendship>(e => e.ActionUserId);

这将创建一对一的关系。您可以使用HasOne(...).WithMany()创建一对多关系。

这会让你超越上述错误。现在您将看到另一个错误,因为类Friendship没有定义主键。虽然文章说创建一个唯一索引,但对于多对多连接表,连接表配置了复合PK,以便它可以表示唯一的连接。因此,您应该使用以下代码,而不是像上面那样调用HasIndex

builder.Entity<Friendship>().HasKey(e => new { e.ApplicationUserId, e.FriendId });

在上面的代码之后,您可以删除HasIndex调用,因为PK始终是唯一的,并且大多数数据库都为PK定义了索引。

通过以上更改,您的模型应该可以正常工作。

其他事情:由于Friendship.ActionUser定义的关系有点模棱两可,因为它是一对一或一对多,或许根本不应该是一种关系。 ActionUserId应采用ApplicationUserIdFriendId之一,您可以通过选择其中一个导航轻松访问ActionUser。您可以在EF中制作ActionUser [NotMapped],并根据ApplicationUser/Friend计算返回值ActionUserId。虽然这些是设计选择。没有正确或错误的方式。

应该使用哪种最有意义并以最消费的方式帮助你。