2

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?

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Cedric Arnould
  • 1,991
  • 4
  • 29
  • 49

2 Answers2

1

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");
}
ZiggZagg
  • 1,397
  • 11
  • 16
0

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);
    }
}
BlackICE
  • 8,816
  • 3
  • 53
  • 91
  • Thanks, It doesnt compile, there is no field Role in User, at least it could be a ICollection named Roles, but it wont be possible to use WithMany after, only WithOne. Maybe, I missed something, Could you explain me more your answer please? – Cedric Arnould Mar 25 '18 at 17:23
  • The recipe from msdn should work, but it didn't. I have the same problem as the author. – Blush Jul 12 '20 at 17:56