In EF Core 2.0 Identity navigation properties are not included by default, so after upgrading, I added them. So for Many-To-Many relationship between User and Role, and One-To-Many relationship between Role and RoleClaim, I added following navigation properties:
public class User : IdentityUser
{
[Required]
public string Name { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
}
public class Role : IdentityRole
{
[Required]
public string Name { get; set; }
public virtual ICollection<IdentityRoleClaim<string>> Claims { get; set;}
}
Surprisingly it adds an additional RoleId1
key to AspNetRoleClaims
table and UserId1
to AspNetUserRoles
tables and all the get queries actually use new keys instead of RoleId
and UserId
which are also present.
I do not know why, there are not these useful navigation properties. I want to list users with their roles.
So I did the follow:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; } = new List<ApplicationUserRole>();
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationRole : IdentityRole<string>
{
public ApplicationRole(){ }
public ApplicationRole(string roleName)
: base(roleName)
{
}
public virtual ICollection<ApplicationUserRole> UserRoles { get; } = new List<ApplicationUserRole>();
}
This creates the navigation, but it creates additional columns like RoleId1
and Discriminator
. So, I added the following according to Add IdentityUser POCO Navigation Properties.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasMany(e => e.UserRoles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<ApplicationUserRole>()
.HasOne(e => e.User)
.WithMany(e => e.UserRoles)
.HasForeignKey(e => e.UserId);
builder.Entity<ApplicationUserRole>()
.HasOne(e => e.Role)
.WithMany(e => e.UserRoles)
.HasForeignKey(e => e.RoleId);
}
But I still have both columns RoleId1
and Discriminator
. After that, I replace with the new ApplicationRole class in ApplicationDbContext, DI configuration service and DB seed.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>
, ApplicationUserRole, IdentityUserLogin<string>, IdentityRoleClaim<string>, IdentityUserToken<string>>
{
...
}
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
}
public DbInitializer(
ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
}
public async void Initialize()
{
_context.Database.EnsureCreated();
if (!_context.Roles.Any(r => r.Name == SharedConstants.Role.ADMINISTRATOR))
await _roleManager.CreateAsync(new ApplicationRole(SharedConstants.Role.ADMINISTRATOR));
}
Also, I could navigate and get the first name of role.
ctx.Users.Select(e => new
{
e.Id,
e.UserName,
e.Email,
e.PhoneNumber,
Roles = e.UserRoles.Select(i => i.Role.Name).ToList()
}).ToList();
I hope this give you a clue for Claims
navigation property.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With