Reputation: 2403
I would like to include the list of roles for users.
var users = Users
.Include(u => u.UserRoles)
.ToList()
I followed many suggestions to fix the problems but not one worked for me:
So probably someone else could be in the same situation.
Startup.cs
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<ApiDbContext>()
.AddDefaultTokenProviders()
;//.AddUserStore<UserStore<User, Role, ApiDbContext, Guid>>()//, IdentityUserClaim<Guid>, UserRole, IdentityUserLogin<Guid>, IdentityUserToken<Guid>, IdentityRoleClaim<Guid>>>()
//.AddRoleStore<RoleStore<Role, ApiDbContext, Guid>>();//, UserRole, IdentityRoleClaim < Guid >>> ();
[Note]: In comment, there is many different tries to make work the migration without success
Role.cs
public class Role : IdentityRole<Guid>
{
public virtual ICollection<UserRole> UserRoles { get; } = new List<UserRole>();
}
User.cs
public class User : IdentityUser<Guid>, IEntity
{
public virtual ICollection<UserRole> UserRoles { get; } = new List<UserRole>();
}
UserRole.cs
public class UserRole : IdentityUserRole<Guid>
{
public virtual User User { get; }
public virtual Role Role { get; }
}
ApiDbContext.cs
public class ApiDbContext : IdentityDbContext<User, Role, Guid, IdentityUserClaim<Guid>, UserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasMany(e => e.UserRoles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<UserRole>()
.HasOne(e => e.User)
.WithMany(e => e.UserRoles)
.HasForeignKey(e => e.UserId);
builder.Entity<UserRole>()
.HasOne(e => e.Role)
.WithMany(e => e.UserRoles)
.HasForeignKey(e => e.RoleId);
base.OnModelCreating(builder);
}
Each time I run 'Add-migration', whatever the suggestion followed, I always have:
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<Guid>(nullable: false),
RoleId = table.Column<Guid>(nullable: false),
RoleId1 = table.Column<Guid>(nullable: true),
UserId1 = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId1",
column: x => x.RoleId1,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId1",
column: x => x.UserId1,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Role1 and User1 are not supposed to be there.
I m using:
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.2" />
Is there someone who has an idea how to fix this issue?
Upvotes: 2
Views: 3292
Reputation: 8926
This is not specific to EF core, but hopefully can help you: https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/fluent/relationships?redirectedfrom=MSDN#configuring-a-many-to-many-relationship
It looks to me like you're overly defining the many to many relationship. Unless you have additional properties needed on the UserRoles table you don't need to directly reference it. I would simplify your ApiDbContext to this:
public class ApiDbContext : IdentityDbContext<User, Role, Guid, IdentityUserClaim<Guid>, UserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasMany(e => e.Role)
.WithMany(r => r.User)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
}
looking at this further, I believe you would have to update user and role as follows, since you don't want to explicitly define the UserRoles table as I mentioned: Role.cs
public class Role : IdentityRole<Guid>
{
public virtual ICollection<User> Users { get; } = new List<User>();
}
User.cs
public class User : IdentityUser<Guid>, IEntity
{
public virtual ICollection<Role> Roles { get; } = new List<Role>();
}
You may also need to update my previous code to fix the names to be plural:
public class ApiDbContext : IdentityDbContext<User, Role, Guid, IdentityUserClaim<Guid>, UserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasMany(e => e.Roles)
.WithMany(r => r.Users)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
}
Upvotes: 0
Reputation: 1427
Try adding setters to the properties of UserRole.
public class UserRole : IdentityUserRole<Guid>
{
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
if you need it to be read only you could add a private constructor instead.
public class UserRole : IdentityUserRole<Guid>
{
private User _user;
private Role _role;
private UserRole()
{
}
public UserRole(User user, Role role)
{
_user = user;
_role = role;
}
public virtual User User => _user;
public virtual Role Role => _role;
}
But then you need to configure the private fields in your dbcontext.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>().Property(u => u.User).HasField("_user");
modelBuilder.Entity<UserRole>().Property(u => u.Role).HasField("_role");
}
Upvotes: 2