Как да създадете функционалност за добавяне на приятел между два отделни потребителски акаунта в една и съща таблица в основна идентичност на asp.net

Разработвам мини приложение за социални медии и използвам ASP.NET Identity за създаване и управление на потребителски акаунти. Искам да добавя друг потребителски акаунт като приятел към моя акаунт. Бих могъл да направя това успешно, но проблемът е, че когато проверих акаунта на моя добавен приятел, няма актуализация в неговия списък с приятели. Празно е.

Ето моя потребителски клас, наследен от 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,

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; }
}

My Controller 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("/");
    }

person ithuRik    schedule 04.08.2020    source източник
comment
Въз основа на отговора на Дейвид Линг и вашата актуализация на оригиналната публикация, актуализирах отговора си.   -  person Dan Barrett    schedule 05.08.2020
comment
@ithuRik: съжалявам, че отнех толкова време. Бях изключително зает на работа до сега. Актуализирах отговора си, за да отразява тази самореферираща връзка много към много, а не просто много към едно, за която мислех преди. Моля, проверете го.   -  person David Liang    schedule 08.08.2020


Отговори (2)


Мисля, че не моделираш правилно своя обект. Тъй като потребителят може да има списък с приятели и също да бъде приятел на други потребители, предполагам, че трябва да заснемете последната част в модела.

Тъй като това е връзка много към много и EF Core все още не поддържа това, без да декларирате обект, който да представлява таблицата за присъединяване, трябва да дефинирате и този обект :

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, което е по подразбиране, за да предотвратите каскадното изтриване.

Отказ от отговорност: Написах всичко на ръка. Никога не го тествайте.

person David Liang    schedule 05.08.2020
comment
Редактирах кода си, както предложихте, сега мога да видя името си и в моя приятелски акаунт, но сега имам нов проблем. Когато добавя двама приятели, името ми е само в акаунта на последния добавен приятел. не е проблем, когато добавих само един приятел. Но когато се опитам да добавя двама или повече приятели, възниква този проблем. Редактирах моя метод на контролер сега по-горе. Можете да го видите. - person ithuRik; 05.08.2020
comment
@ithuRik: Упс, предположих, че можеш да имаш списък с приятели, но можеш да си приятел само на един човек. Ето защо моделирах само FriendOf като едно към много. Ако наистина е много към много, може да се наложи да създадем междинна таблица за свързване. Не съм сигурен дали подкрепяте сценарий, при който вие сте мой приятел, но аз не съм ваш приятел? - person David Liang; 06.08.2020

Ще трябва да използвате функцията Включване.

// 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

РЕДАКТИРАНЕ:

Погледнете тази публикация, тя е дубликат на тази.

самореферираща връзка много към много

РЕДАКТИРАНЕ 2: Така че проблемът, който имате, е, че все още се опитвате да моделирате това като една таблица. Помислете как това ще бъде структурирано в SQL база данни. Как може една таблица да съдържа колекция (Приятел) в една колона? За да постигнем това, ще създадем нова таблица, за да моделираме връзката между 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 );
    });        
}

На този етап трябва да можете да правите заявки към таблицата за присъединяване за приятелства.

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 ();
}
person Dan Barrett    schedule 04.08.2020
comment
благодаря, че ми помогна. Този пост наистина ми помогна да разбера какъв е проблемът. Но когато опитам това в кода си, той показва грешката Типът на обекта „Приятели“ изисква да бъде дефиниран първичен ключ. Ако възнамерявате да използвате тип обект без ключ, извикайте „HasNoKey()“. - person ithuRik; 05.08.2020
comment
Сега коригирах грешката на първичния ключ, като добавих свойство HasKey към modelbuilder, но все още не можах да актуализирам името си в списъка с приятели. Мисля, че нещо не е наред с методите ми на контролера. От седмица си скубя косата заради този проблем и все още не намирам решението. - person ithuRik; 05.08.2020