You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Entity Framework Core中更直接地为IdentityUser间的好友关系建模

问题根源

  1. 自引用多对多关系配置错误:你在HasMany和WithMany中都指向了同一个Friends集合,EF Core无法区分关系的两端方向,导致生成中间表实体时无法正确赋值主键字段。
  2. 异步方法未加await:你调用GetUserAsync和FindByIdAsync时没有等待异步返回,拿到的是Task<BlogUser>对象而非实际的用户实体,EF Core读取不到ID值,所以抛出User1Id为空的异常。
  3. 导航属性未手动加载:EF Core默认不会自动加载关联导航属性,你直接通过UserManager获取的用户对象,Friends集合不会自动填充,所以查询返回空数组。
  4. 调换配置后返回自己:是因为关系方向配置颠倒,EF Core把中间表的User1Id对应的用户当作了好友,自然查询出当前用户自己。

解决方案

步骤1:调整实体定义

为自引用多对多区分双向导航,不要复用同一个Friends集合:

public class BlogUser : IdentityUser
{
    public virtual ICollection<Blog> Blogs { get; set; }
    // 我关注的好友列表
    public virtual ICollection<BlogUser> Friends { get; set; }
    // 关注我的粉丝列表
    public virtual ICollection<BlogUser> Followers { get; set; }
    public virtual ICollection<BlogUserFriendship> Friendships { get; set; }
}

BlogUserFriendship实体不需要改动。

步骤2:修正关系配置

protected override void OnModelCreating(ModelBuilder builder)
{
    // 必须调用基类方法,保证Identity相关配置生效
    base.OnModelCreating(builder);

    builder.Entity<BlogUser>()
        // 左集合:当前用户的好友
        .HasMany(u => u.Friends)
        // 右集合:当前用户的粉丝
        .WithMany(u => u.Followers)
        .UsingEntity<BlogUserFriendship>(
            j => j
                .HasOne(fs => fs.User2)
                .WithMany()
                .HasForeignKey(fs => fs.User2Id)
                .OnDelete(DeleteBehavior.Restrict),
            j => j
                .HasOne(fs => fs.User1)
                .WithMany(u => u.Friendships)
                .HasForeignKey(fs => fs.User1Id),
            j =>
            {
                j.HasKey(fs => new { fs.User1Id, fs.User2Id });
            }
        );
}

步骤3:修正添加好友的代码

注意补全await,不需要用UserManager.UpdateAsync,直接通过DbContext保存即可:

// 等待异步方法返回实际用户实体
var user = await _userManager.GetUserAsync(this.User);
var friendToAdd = await _userManager.FindByIdAsync(model.UserId);

// 附加当前用户到上下文,加载Friends集合
_context.Users.Attach(user);
user.Friends ??= new List<BlogUser>();
user.Friends.Add(friendToAdd);

// 如果是双向好友(加好友需要对方同意的场景不用加这行)
// friendToAdd.Friends.Add(user);

await _context.SaveChangesAsync();

步骤4:修正查询好友的代码

手动Include加载Friends导航属性:

[Route("Friends")]
[HttpGet]
public async Task<IActionResult> GetFriends()
{
    var userId = _userManager.GetUserId(this.User);
    var user = await _context.Users
        .Include(u => u.Friends)
        .FirstOrDefaultAsync(u => u.Id == userId);
    return Ok(user.Friends.Select(f => new {f.UserName}).ToList());
}

可选优化

如果你的场景是双向好友关系(即A是B的好友,B也自动是A的好友),可以在业务层封装好友添加逻辑,插入中间表时同时插入(A,B)和(B,A)两条记录,或者查询时合并Friends和Followers集合返回即可。


内容的提问来源于stack exchange,提问作者DHao2001

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 19:36:01