如何在 EF Core 5 中映射 Twitter 关注者/关注类型的关系

问题描述

如何使用 EF Core 5 和 Fluent API 配置类似于 Twitter 关注和关注者类型的关系?我尝试了各种不同的配置方法,唯一能让它工作的方法是忽略 User 实体上的导航属性。我目前正在将我的代码从 EF Core 2.1 迁移到 5。以下配置更早运行。 (不确定是不是配置错误

    public class User
    {
        public long Id { get; set; }

        public string Name { get; set; }

        public ICollection<UserFollower> Followers { get; set; }

        public ICollection<UserFollower> Following { get; set; }
    }

    public class UserFollower
    {
        public long UserId { get; set; }

        public User User { get; set; }

        public long FollowedById { get; set; }

        public User FollowedBy { get; set; }
    }

    public class UserFollowerConfiguration : IEntityTypeConfiguration<UserFollower>
    {
        public void Configure(EntityTypeBuilder<UserFollower> builder)
        {
            builder.HasKey(p => new { p.UserId,p.FollowedById });
            builder.HasOne(p => p.User)
                .WithMany(i => i.Followers)
                .HasForeignKey(i => i.UserId);
            builder.HasOne(p => p.FollowedBy)
                .WithMany(i => i.Following)
                .HasForeignKey(i => i.FollowedById);
        }
    }

此配置在保存到数据库时抛出错误

sqlException: Violation of PRIMARY KEY constraint 'PK_UserFollower'. 
Cannot insert duplicate key in object 'dbo.UserFollower'. The duplicate key value is (111,111).

即使尝试直接添加到 DbContext 并对其调用 SaveChanges()

Context.Add(new UserFollower() {UserId = 222,FollowedById = 111});

映射与 EF Core 5 的这种关系的推荐方式是什么?请注意,我确实需要访问 UserFollowers 表,而无需通过用户的导航属性

编辑 #1

以下是 DbContext 的 OnModelCreating()

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfigurations(typeof(DbContext).Assembly);
        
        /*few configurations unrelated to UserFollower entity*/
    }

用户实体有如下配置,

    builder.HasKey(i => i.Id);
    builder.Property(i => i.Id).ValueGeneratedOnAdd();

解决方法

尝试像这样配置它。

builder.Entity<User>().HasMany(s => s.Followers)
    .WithOne(f => f.FollowedBy);
builder.Entity<User>().HasMany(s => s.Following)
    .WithOne(f => f.);

此外,UserFollower 表中缺少 PK,我不知道是否在某处以某种方式生成了 Id。如果没有,也许这就是为什么它试图错误地使用 FollowedById 作为键,但为 UserFollower 表定义一个 Id 并查看。

public class UserFollower
{
    public long Id {get;set;} 

    public long UserId { get; set; }

    public User User { get; set; }

    public long FollowedById { get; set; }

    public User FollowedBy { get; set; }
 }

即使这样可行,我还是建议您更改模型的结构,但对于您描述的 twitter 要求来说,它看起来不明确。如果我查询 Userfollowers

var userFollowers = _context.UserFollowers.ToList();

对于列表中的每个结果,我无法判断用户是在关注还是被关注。您可以将模型更改为这些模型;

public class User
{
    public long Id { get; set; }

    public string Name { get; set; }

    public ICollection<UserFollower> Followers { get; set; }

    public ICollection<UserFollowing> Following { get; set; }
}

public class UserFollower
{
    public long UserId { get; set; }

    public User User { get; set; }

    public long UserFollowingMeId { get; set; }

    public User UserFollowingMe { get; set; }
}
public class UserFollowing
{
    public long UserId { get; set; }

    public User User { get; set; }

    public long UserIAmFollowingId { get; set; }

    public User UserIAmFollowing { get; set; }
}

这样,每个人都知道当他们检查 UserFollowings 表时,UserId 是关注者的 ID,对于 UserFollowers 表,反之亦然。如果我在系统中的Id为8,我可以这样查询我的关注者和我关注的人;

var myFollowers = _context.UserFollowers.Where(UserId = 8);
var peopleIFollow = _context.UserFollowing.Where(UserId = 8);