Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP Net Core: add many to many relationship with IdentityUser

i need to add a many to many relationship with UserIdentity in asp net core (i.e: a user can have many books and a book can have many user owners)

I have the book class:

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

I have extended the UserIdentity class:

public class ApplicationUser : IdentityUser
{
    public ICollection<UserBook> UserBooks { get; set; }
}

I have created the join table but i don't know how reference the identityuser id

public class UserBook
{
    public int UserId { get; set; }
    public ApplicationUser ApplicationUser { get; set; }
    public int BookId { get; set; }
    public Book Book { get; set; }
}

How can create correctly a many to many relationship between the identityuser table and another table?

Thanks

like image 537
Tom Avatar asked Jan 26 '23 18:01

Tom


2 Answers

In your UserBook model class you have used UserId of type int for ApplicationUser but in your ApplicationUser model class you are inheriting IdentityUser where primary key Id is of type string by default, that means your ApplicationUser's Id is of type string. So there will be a primary key type mismatch for Foreignkey in UserBook table.

Solution-1: If you have no problem to keep ApplicationUser's primary key Id of type string, then just change the UserId type to string in UserBook model class as follows:

public class UserBook
{
    public string UserId { get; set; }
    public ApplicationUser ApplicationUser { get; set; }
    public int BookId { get; set; }
    public Book Book { get; set; }
}

Solution-2: If you want to change ApplicationUser's primary key Id from default string type to int, then specify the key type as int while you are inheriting IdentityUser as follows:

public class ApplicationUser : IdentityUser<int>
{
   public ICollection<UserBook> UserBooks { get; set; }
}

Now you have to make changes in ConfigureServices method of the Startup class as follows:

services.AddDefaultIdentity<IdentityUser<int>>() // here replace `IdentityUser` with `IdentityUser<int>`
        .AddDefaultUI()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

Now finally your model configuration (for both Solution-1 and Solution-2) using Fluent Api should be as follows:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<UserBook>()
        .HasKey(ub => new { ub.UserId, ub.BookId });

    modelBuilder.Entity<UserBook>()
        .HasOne(ub => ub.ApplicationUser)
        .WithMany(au => au.UserBooks)
        .HasForeignKey(ub => ub.UserId);

    modelBuilder.Entity<UserBook>()
        .HasOne(ub => ub.Book)
        .WithMany() // If you add `public ICollection<UserBook> UserBooks { get; set; }` navigation property to Book model class then replace `.WithMany()` with `.WithMany(b => b.UserBooks)`
        .HasForeignKey(ub => ub.BookId);
}
like image 192
TanvirArjel Avatar answered Feb 01 '23 17:02

TanvirArjel


Simple, public string ApplicationUserId { get; set; }

ApplicationUser has a default key type of string.

If you do want to use int, simple do:

public class ApplicationUser : IdentityUser<int>

Be sure to declare this relationship in your DbContext.

Here's a sample:

entityTypeBuilder.HasKey(pcp => new { pcp.CurrencyPairId, pcp.IsMain })
                .HasName("PartialCurrencyPair_CK_CurrencyPairId_IsMain");

            entityTypeBuilder.HasOne(pcp => pcp.Currency).WithMany(c => c.PartialCurrencyPairs)
                .HasForeignKey(pcp => pcp.CurrencyId)
                .HasConstraintName("PartialCurrencyPairs_Currency_Constraint");
            entityTypeBuilder.HasOne(pcp => pcp.CurrencyPair).WithMany(cp => cp.PartialCurrencyPairs)
                .HasForeignKey(pcp => pcp.CurrencyPairId)
                .HasConstraintName("PartialCurrencyPairs_CurrencyPair_Constraint");

EDIT

You don't have to explicitly define the generics together with the base class (IdentityUser). You simply do this:

services.AddIdentity<ApplicationUser, Role>()
                .AddEntityFrameworkStores<NozomiAuthContext>()
                .AddDefaultTokenProviders();

This assumes Role and ApplicationUser share the same key type of string, where they are overloads of IdentityRole and IdentityUser respectively.

like image 38
Nicholas Avatar answered Feb 01 '23 16:02

Nicholas