英文:
Adding users to roles in ASP.Net Identity with EF 6 causes new roles and users to be created
问题
我遇到了问题,希望有人能提供答案。我创建了一个带有 Identity 的 Blazor 应用程序,以下是我的 dbContext
看起来是这样的:
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, string,
AppUserClaim, AppUserRole, AppUserLogin, AppRoleClaim, AppUserToken>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<AppUser>().ToTable("Users");
builder.Entity<AppUser>().Property(x => x.FirstName).IsRequired();
builder.Entity<AppUser>().Property(x => x.LastName).IsRequired();
builder.Entity<AppUser>().Property(x => x.LastLoginDate);
builder.Entity<AppUser>().HasMany(x => x.Roles).WithOne(x => x.User).HasForeignKey(x => x.UserId).IsRequired();
builder.Entity<AppUserRole>().ToTable("UserRoles").HasOne(c => c.Role).WithMany(c => c.UserRoles).HasForeignKey(c => c.UserId);
builder.Entity<AppRole>().ToTable("Roles");
builder.Entity<AppRole>().Property(x => x.Description);
builder.Entity<AppRole>().HasMany(x => x.UserRoles).WithOne(e => e.Role).HasForeignKey(ur => ur.RoleId).IsRequired();
builder.Entity<AppRole>().HasMany(x => x.RoleClaims).WithOne(e => e.Role).HasForeignKey(x => x.RoleId).IsRequired();
builder.Entity<AppRoleClaim>().ToTable("RoleClaims");
builder.Entity<AppRoleClaim>().HasOne(x => x.Role);
builder.Entity<AppUserClaim>().ToTable("UserClaims");
}
}
在我的 program.cs
中,我有以下代码:
builder.Services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddTransient<RoleManager<AppRole>>();
以下是我的 Identity 类:
public class AppRole : IdentityRole
{
public string? Description { get; set; }
public virtual ICollection<AppUserRole>? UserRoles { get; set; }
public virtual ICollection<AppRoleClaim>? RoleClaims { get; set; }
public AppRole()
{
}
}
public class AppRoleClaim : IdentityRoleClaim<string>
{
public virtual AppRole Role { get; set; }
public AppRoleClaim()
{
Role = new AppRole();
}
}
public class AppUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTimeOffset? LastLoginDate { get; set; }
public byte[]? Photo { get; set; }
public virtual ICollection<AppUserRole> Roles { get; set; }
public AppUser()
{
FirstName = "";
LastName = "";
Roles = new List<AppUserRole>();
}
}
public class AppUserClaim : IdentityUserClaim<string>
{
}
public class AppUserLogin : IdentityUserLogin<string>
{
}
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser User { get; set; }
public virtual AppRole Role { get; set; }
public AppUserRole()
{
User = new AppUser();
Role = new AppRole();
}
}
public class AppUserToken : IdentityUserToken<string>
{
}
这是我的表结构:
最后,我的问题是,当我调用 UserManager.CreateAsync(NewUser)
时,用户会正确创建,但是当我尝试将新创建的用户添加到一个角色中,例如管理员角色,如下所示 await UserManager.AddToRoleAsync(NewUser, "Admin");
它不会抛出任何错误,但发生的情况是它创建一个新的用户,用户名、姓氏等都为 null,然后将一个具有 null 名称和 null 标准化名称的新角色添加到角色表中。然后将该角色添加到 UserRoles
表中,所有这些操作都没有出现任何错误。
根据微软的文档,这应该创建一个新用户并将该用户添加到 Admin 角色中。我是根据以下文档构建的:https://learn.microsoft.com/en-us/aspnet/core/security/?view=aspnetcore-6.0
英文:
I have hit a wall on this and hope someone else has the answer, I created a Blazor app with Identity this is what my dbContext
looks like:
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, string,
AppUserClaim,AppUserRole,AppUserLogin,AppRoleClaim,AppUserToken>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<AppUser>().ToTable("Users");
builder.Entity<AppUser>().Property(x => x.FirstName).IsRequired();
builder.Entity<AppUser>().Property(x => x.LastName).IsRequired();
builder.Entity<AppUser>().Property(x => x.LastLoginDate);
builder.Entity<AppUser>().HasMany(x => x.Roles).WithOne(x=>x.User).HasForeignKey(x=>x.UserId).IsRequired();
builder.Entity<AppUserRole>().ToTable("UserRoles").HasOne(c => c.Role).WithMany(c => c.UserRoles).HasForeignKey(c => c.UserId);
builder.Entity<AppRole>().ToTable("Roles");
builder.Entity<AppRole>().Property(x => x.Description);
builder.Entity<AppRole>().HasMany(x => x.UserRoles).WithOne(e => e.Role).HasForeignKey(ur => ur.RoleId).IsRequired();
builder.Entity<AppRole>().HasMany(x => x.RoleClaims).WithOne(e=>e.Role).HasForeignKey(x=>x.RoleId).IsRequired();
builder.Entity<AppRoleClaim>().ToTable("RoleClaims");
builder.Entity<AppRoleClaim>().HasOne(x => x.Role);
builder.Entity<AppUserClaim>().ToTable("UserClaims");
}
}
And in my program.cs
, I have this code:
builder.Services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddTransient<RoleManager<AppRole>>();
Here are my Identity classes:
public class AppRole:IdentityRole
{
public string? Description { get; set; }
public virtual ICollection<AppUserRole>? UserRoles { get; set; }
public virtual ICollection<AppRoleClaim>? RoleClaims { get; set; }
public AppRole()
{
}
}
public class AppRoleClaim : IdentityRoleClaim<string>
{
public virtual AppRole Role { get; set; }
public AppRoleClaim()
{
Role = new AppRole();
}
}
public class AppUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTimeOffset? LastLoginDate { get; set; }
public byte[]? Photo { get; set; }
public virtual ICollection<AppUserRole> Roles { get; set; }
public AppUser()
{
FirstName = "";
LastName = "";
Roles = new List<AppUserRole>();
}
}
public class AppUserClaim : IdentityUserClaim<string>
{
}
public class AppUserLogin : IdentityUserLogin<string>
{
}
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser User { get; set; }
public virtual AppRole Role { get; set; }
public AppUserRole()
{
User = new AppUser();
Role = new AppRole();
}
}
public class AppUserToken : IdentityUserToken<string>
{
}
And this is my table structure:
And finally my question , when I call UserManager.CreateAsync(NewUser)
a user is correctly created, however when I try to add the newly created user to a role for example the admin role like so await UserManager.AddToRoleAsync(NewUser, "Admin");
it does not throw any error, but what happens is it creates a new user with null user name, first name etc and it adds a new role to the roles table with a null Name and null normalized name. Then it adds that role to the UserRoles
table. All without any errors.
According to MS Documentation this should create one new user and add that user to the Admin role. Docs I built this from are here https://learn.microsoft.com/en-us/aspnet/core/security/?view=aspnetcore-6.0
答案1
得分: 0
在今天的一些调查后,我弄清楚了发生了什么事情。看一下我的 AppUserRole
类
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser User { get; set; }
public virtual AppRole Role { get; set; }
public AppUserRole()
{
User = new AppUser();
Role = new AppRole();
}
}
由于我新建了一个新的 AppUser()
和一个新的 AppRole()
,它们依次调用了基类构造函数,其中 IdentityRole
将其 Id
属性设置为一个新的 Guid
,当调用 IdentityUser
的构造函数时,同样会发生这种情况。我通过查看 AspNetCore.Identity
源代码找到了这一点。因此,EntityFramework 正确执行了其工作,它看到一个全新的 AppUserRole
,带有新的 IdentityUser
和新的 IdentityRole
,由于这些是指向 AppRole
的导航属性,它在数据库中创建了它们。
简而言之,修复方法是使 AppUserRole
类中的导航属性可为空,并不在构造函数中创建它们,如下所示:
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser? User { get; set; }
public virtual AppRole? Role { get; set; }
public AppUserRole()
{
}
}
英文:
After doing some digging today I figured out what was going on. Have a look at my AppUserRole
class
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser User { get; set; }
public virtual AppRole Role { get; set; }
public AppUserRole()
{
User = new AppUser();
Role = new AppRole();
}
}
Since I was newing up a new AppUser()
and a new AppRole()
they in turn were calling the base class constructors in which IdentityRole
sets its Id
property to a new Guid
the same thing happens when the constructor of IdentityUser
is called. I found that out by looking at the AspNetCore.Identity
source code. So EntityFramework was correctly doing its job, it saw a brand new AppUserRole
with a new IdentityUser
and new IdentityRole
and since these were navigation properties to AppRole
it created them in the database.
TLDR The fix was to make the navigation properties in the AppUserRole
class nullable and not new them up in the constructor as in below:
public class AppUserRole : IdentityUserRole<string>
{
public virtual AppUser? User { get; set; }
public virtual AppRole? Role { get; set; }
public AppUserRole()
{
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论