如何在 asp.net 核心身份中相同 table 的两个个人用户帐户之间创建添加好友功能

How to create an Add Friend functionality between two individual user accounts in the same table in asp.net core identity

我正在开发一个迷你社交媒体网络应用程序,我使用 ASP.NET Identity 来创建和管理用户帐户。 我想将另一个用户帐户添加为我帐户的好友。我可以成功地做到这一点,但问题是当我检查我添加的朋友的帐户时,他的朋友列表中没有更新。它是空的。

这是我的用户 class 继承自 IdentityUser,

public class AppUser : IdentityUser
{
    public AppUser()
    {
        this.Friends = new HashSet<AppUser>();
    }
    public int Age { get; set; }
    public string Sex { get; set; }
    public string City { get; set; }
    public string Education { get; set; }
    public string Description { get; set; }
    public string? FriendOfUserId { get; set; }
    public virtual AppUser FriendOf { get; set; }

    public ICollection<AppUser> Friends { get; set; }
}

我的 DbContext class,

public class ApplicationDbContext : IdentityDbContext<AppUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
builder.Entity<AppUser>(x =>
        {
            x
                .HasMany(x => x.Friends)
                .WithOne(x => x.FriendOf)
                .HasForeignKey(x => x.FriendOfUserId);
        });
    }
    public DbSet<AppUser> Users { get; set; }
}

我的控制器Class添加好友的方法,

public async Task<IActionResult> AddFriend(string id)
    {
        var addFriend = await context.Users.Include(u => u.Friends).FirstOrDefaultAsync(u => u.Id == id);
        var user = await userManager.GetUserAsync(this.User);
        var u = await context.Users.Include(u => u.Friends).FirstOrDefaultAsync(u => u.Id == user.Id);
        user.FriendOf = addFriend;
        user.Friends.Add(addFriend);
        await context.SaveChangesAsync();
        return Redirect("/");
    }

您将需要使用包含功能。

// What you have is fine.
var friend = context.Users.Select ( u => u == id );

// this is what needs to occur in await userManager.GetUserAsync(this.User);
// Without the include the Navigation Property will not be tracked.
var user = context.Users
    .Select ( u => u == id )
    .Include ( u => u.Friends );

user.Friends.Add ( friend );
context.SaveChanges ();

检查加载相关数据。 https://docs.microsoft.com/en-us/ef/core/querying/related-data

编辑:

看看这个post它是这个的副本。

编辑 2: 所以您遇到的问题是您仍在尝试将其建模为一个 table。想想这将如何在 SQL 数据库中构建。单个 table 如何在单个列中包含一个集合(Friend)?为此,我们将创建一个新的 table 来模拟 AppUsers 之间的关系。

public class Friendship 
{
    // Foreign Key
    Int32 MeId { get; set; }

    // Foreign Key
    Int32 FriendId { get; set; }

    // Navigation Property
    AppUser Me { get; set; }

    // Navigation Property
    AppUser Friend { get; set; }
}
public class AppUser : IdentityUser
{
    public AppUser()
    {
        this.Friends = new HashSet<AppUser>();
    }
    
    // Primary Key. Is this defined in IdentityUser?
    public int Id { get; }
    public int Age { get; set; }
    public string Sex { get; set; }
    public string City { get; set; }
    public string Education { get; set; }
    public string Description { get; set; }

    // This is considered a Navigation Property 
    public ICollection<Friendship> Friends { get; set; }
}
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<AppUser>(x =>
    {
        x.HasPrimaryKey ( x => x.Id );
    });

    builder.Entity<Friendship>( f =>
    {
        f.HasKey ( f => new { f.MeId, f.FriendId } );
        f
            .HasOne( f => f.Me )
            .WithMany ( u => u.Friends )
            .HasForeignKey( f => f.MeId );

        f
            .HasOne( f => f.Friend )
            .WithMany ( u => u.Friends )
            .HasForeignKey( f => f.FriendId );
    });        
}

此时您应该可以查询加入 table 的友谊。

public void AddFriend ( AppUser user, AppUser friend ) {
    var trackedUser = context.AppUsers
         .Select ( u => u.Id == user.Id )
         .Include ( u => u.Friends );          
         .FirstOrDefault ();

   trackedUser.Friends.Add ( new Friendship () { 
       MeId = user.Id, 
       FriendId = friend.Id 
   });
   context.SaveChanges ();
}

我认为您没有正确建模您的实体。由于一个用户可以有一个朋友列表,也可以是其他用户的朋友,我想你需要在模型中捕获后面的部分。

由于这是一个 many-to-many 关系,并且 EF Core 仍然不支持 it without declaring an entity to represent the join table,您还需要定义该实体:

public class AppUser : IdentityUser
{
    public int Age { get; set; }
    public string Sex { get; set; }
    public string City { get; set; }
    public string Education { get; set; }
    public string Description { get; set; }

    public ICollection<AppUserFriendship> FriendsOf { get; set; }
    public ICollection<AppUserFriendship> Friends { get; set; }
}

public class AppUserFriendship
{
    public string UserId { get; set; }
    public AppUser User { get; set; }

    public string UserFriendId { get; set; }
    public AppUser UserFriend { get; set; }
}

然后你需要配置他们的关系:

public class ApplicationDbContext : IdentityDbContext<AppUser>
{
    ...

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<AppUserFriendship>(b =>
        {
            b.HasKey(x => new { x.UserId, x.UserFriendId };

            b.HasOne(x => x.User)
                .WithMany(x => x.Friends)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Restrict);

            b.HasOne(x => x.UserFriend)
                .WithMany(x => x.FriendsOf)
                .HasForeignKey(x => x.UserFriendId)
                .OnDelete(DeleteBehavior.Restrict);
        });
    }

    public DbSet<AppUser> Users { get; set; }
}

备注OnDelete(DeleteBehavior.Restrict)。您必须将其设置为 DeleteBehavior.Cascade 以外的值,这是防止级联删除的默认值。

免责声明:我都是手写的。永远不要测试它。