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

How to implement an self referencing many to many relationship with Entity Framework Core 1.1?

我正在按照本教程使用 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);         
}

添加迁移 InitialMigration 的结果

Unable to determine the relationship represented by navigation property 'Friendship.ActionUser' of type 'ApplicationUser'. Either manually configure the relationship, or ignore this property from the model.

此外,由于 EF Core 发展迅速,我发现了很多不同的方法来实现这一点。我不确定我如何实现自引用多对多关系,有人可以给我一些建议吗?

  1. 如何定义 Friendship.ActionUser 和 ApplicationUser 之间的关系?
  2. 关于实现这种自引用多对多关系的正确方法有什么建议吗? EF Core 正在快速发展,我在网上找到了很多不同的方法,但它们似乎已经过时了

谢谢! :)

理想情况下,一旦关系发现中的歧义得到解决,EF 应该按照惯例创建其余的关系,但由于错误而没有发生。 (归档 Bug

您的模型 classes 对于您正在尝试做的事情是正确的。要使 EF 成功构建模型,需要填写的部分很少。

首先,让我们解决您看到的异常。 您的 ApplicationUser class 有 2 个 collection 导航指向 Friendship。并且 Friendship class 有 3 个参考导航指向 ApplicationUser。虽然 EF Core 在按照约定创建关系方面做得很好,但在这种情况下,它不知道如何创建 navigation-inverse 导航对。因此需要用户通过 annotations/fluent API 输入。在您的情况下,您正在使用 fluent API 创建 2 个关系,每侧使用 2 个导航。这留给我们的只有导航 Friendship.ActionUser 而没有任何关系。此时,EF Core 对如何从中创建关系没有任何困惑,但由于错误,它没有这样做。这意味着您必须使用 fluent API.

手动配置此关系
builder.Entity<Friendship>().HasOne(e => e.ActionUser).WithOne().HasForeignKey<Friendship>(e => e.ActionUserId);

这将创建 one-to-one 关系。您可以使用 HasOne(...).WithMany() 创建 one-to-many 关系。

这将使您克服上述错误。现在您将看到另一个错误,因为 class Friendship 没有定义主键。虽然文章说要创建唯一索引,但是对于 many-to-many 连接 table,连接 table 配置为具有复合 PK,以便它可以表示唯一连接。因此,与其像上面那样调用 HasIndex,不如使用以下代码。

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

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

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

其他:由于Friendship.ActionUser定义的关系是one-to-one或one-to-many有点模棱两可,也许它根本不应该是关系。 ActionUserId 应采用 ApplicationUserIdFriendId 的值之一,您可以通过选择这些导航之一轻松访问 ActionUser。您可以在 EF 中创建 ActionUser [NotMapped] 并使用基于 ActionUserId 返回的值 ApplicationUser/Friend 进行计算。尽管这些是设计选择。没有正确或错误的方法。应该使用最有意义并且对您的消费方式最有帮助的那个。