EF Core 2.0 Identity - Adding navigation properties

后端 未结 3 1247
面向向阳花
面向向阳花 2021-01-02 21:58

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

相关标签:
3条回答
  • 2021-01-02 22:33

    I've encountered the same. In my case the problem was caused because I had put the call to base.OnModelCreating(builder) at the bottom of OnModelCreating. Moving base.OnModelCreating(builder) at the very top solved the issue (no duplicate columns and FKs).

    Thanks to this GutHub issue

    0 讨论(0)
  • 2021-01-02 22:37

    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.

    0 讨论(0)
  • 2021-01-02 22:39

    I encountered with same issue and this is solution.

    You have to tell Ef on which navigation property you are going to have OneToMany Relation.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
    
            CreateUserModel(modelBuilder.Entity<User>());
            CreateRoleModel(modelBuilder.Entity<Role>());
    
        }
    
    
        private void CreateRoleModel(EntityTypeBuilder<Role> entityTypeBuilder)
        {
            entityTypeBuilder.HasMany(role => role.UserRoles).
                WithOne(**e=> e.Role**).
                HasForeignKey(userRole => userRole.RoleId).
                IsRequired()
                .OnDelete(DeleteBehavior.Cascade);
        }
    
        private void CreateUserModel(EntityTypeBuilder<User> entityTypeBuilder)
        {
            entityTypeBuilder.HasMany(user => user.UserRoles).
                WithOne(**e=>e.User**).
                HasForeignKey(userRole => userRole.UserId).
                IsRequired()
                .OnDelete(DeleteBehavior.Cascade);
        }
    

    You can Also specify navigation property in string like

    private void CreateRoleModel(EntityTypeBuilder<Role> entityTypeBuilder)
        {
            entityTypeBuilder.HasMany(role => role.UserRoles).
                WithOne(**"Role"**).
                HasForeignKey(userRole => userRole.RoleId).
                IsRequired()
                .OnDelete(DeleteBehavior.Cascade);
        }
    
        private void CreateUserModel(EntityTypeBuilder<User> entityTypeBuilder)
        {
            entityTypeBuilder.HasMany(user => user.UserRoles).
                WithOne(**"User"**).
                HasForeignKey(userRole => userRole.UserId).
                IsRequired()
                .OnDelete(DeleteBehavior.Cascade);
        }
    
    0 讨论(0)
提交回复
热议问题