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