Cedric Arnould
Cedric Arnould

Reputation: 2403

EF Core Add Roles to User

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

Answers (2)

BlackICE
BlackICE

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

ZiggZagg
ZiggZagg

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

Related Questions