Entity Framework Core: Fluent api many-to-many

asp.net asp.net-core c# entity-framework-core

Question

How do I model the following: a user has many followers and follows many users. The same user has many blocked users (Twitter kinda feature).

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<User> Following { get; set; }
    public virtual ICollection<User> Followers { get; set; }
    public virtual ICollection<User> BlockedUsers { get; set; }
}

public class User
{
    public ApplicationUser User { get; set; }
    public string UserId { get; set; }
    public ApplicationUser Follower { get; set; }
    public string FollowerId { get; set; }
}

My implementation so far:

public void Configure(EntityTypeBuilder<User> builder)
{
    builder.HasKey(k => new { k.UserId, k.FollowerId });

    builder.HasOne(l => l.User)
           .WithMany(a => a.Followers)
           .HasForeignKey(l => l.UserId);

    builder.HasOne(l => l.Follower)
           .WithMany(a => a.Following)
           .HasForeignKey(l => l.FollowerId);
}

How do I implement the blocked users field?

public virtual ICollection<User> BlockedUsers { get; set; }
1
4
5/29/2018 10:52:16 AM

Accepted Answer

As discussed in chat, I rarely trust EF's capabilities for Many-to-Many, nevermind EF Core. This isn't a direct to your question but instead explains how I'd handle this if it were my project.

You have your ApplicationUser already, so the tables that come off them would only exist to define the relationships between differing ApplicationUsers. Each User can have multiple everything: Followers, Following, and Blocked. A user is not directly in control of who follows them though, so that doesn't need its own table. You can determine who follows a user by looking at the Follower table.

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<UserFollow> Following { get; set; }
    public virtual ICollection<UserFollow> Followers { get; set; }
    public virtual ICollection<UserBlock> BlockedUsers { get; set; }
}

public class UserFollow
{
    public int Id { get; set; }

    [ForeignKey(nameof(SourceUserId))]
    public ApplicationUser SourceUser { get; set; }
    public string SourceUserId { get; set; }

    [ForeignKey(nameof(FollowedUserId))]
    public ApplicationUser FollowedUser { get; set; }
    public string FollowedUserId { get; set; }
}

public class UserBlock
{
    public int Id { get; set; }

    [ForeignKey(nameof(SourceUserId))]
    public ApplicationUser SourceUser { get; set; }
    public string SourceUserId { get; set; }

    [ForeignKey(nameof(BlockedUserId))]
    public ApplicationUser BlockedUser { get; set; }
    public string BlockedUserId { get; set; }
}

Your configuration then doesn't change much (consider this psuedo, untested):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    moderlBuilder.Entity<UserFollow>()
           .HasOne(l => l.SourceUser)
           .WithMany(a => a.Following)
           .HasForeignKey(l => l.SourceUserId);

    moderlBuilder.Entity<UserFollow>()
           .HasOne(l => l.FollowedUser)
           .WithMany(a => a.Followers)
           .HasForeignKey(l => l.FollowedUserId);

    moderlBuilder.Entity<UserBlock>()
           .HasOne(l => l.SourceUser)
           .WithMany(a => a.BlockedUsers)
           .HasForeignKey(l => l.SourceUserId);
}

(note I've always whacked on a simple key (Id just for ease of querying) but you can change it back to be composite as needed)

3
5/30/2018 10:01:44 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow