简体   繁体   English

将管理员用户播种到数据库 OnModelCreating

[英]Seed admin user to database OnModelCreating

I have been browsing internet the whole day and tried whatever I could already.我整天都在浏览互联网,尽我所能尝试。 I have no errors and user is created into database but there is no UserRole assigned.我没有错误,并且用户已创建到数据库中,但没有分配 UserRole。 Also roles are created just fine and can be visible in database.角色也被创建得很好,并且可以在数据库中可见。 Why so?为什么这样? Can somebody see what's the problem in here?有人能看出这里有什么问题吗?

   protected override void OnModelCreating(ModelBuilder builder)
    {
      base.OnModelCreating(builder);

      Guid ADMIN_ID = Guid.NewGuid();
      Guid ROLE_ID = ADMIN_ID;

      builder.Entity<ApplicationRole>().HasData(new ApplicationRole { Name = "User", NormalizedName = "USER", Id = Guid.NewGuid(), ConcurrencyStamp = Guid.NewGuid().ToString() });
      builder.Entity<ApplicationRole>().HasData(new ApplicationRole { Name = "Admin", NormalizedName = "ADMIN", Id = ROLE_ID, ConcurrencyStamp = ROLE_ID.ToString() });

      PasswordHasher<ApplicationUser> hasher = new PasswordHasher<ApplicationUser>();
      builder.Entity<ApplicationUser>().HasData(new ApplicationUser
      {
        Id = ADMIN_ID,
        FirstName = "MrJack",
        LastName = "Jackson",
        UserName = "Administrator",
        NormalizedUserName = "Administrator",
        Email = "admin@admin.com",
        IsActive = true,
        ProfilePicture = System.IO.File.ReadAllBytes(string.Concat(Path.GetFullPath("Resources\\"), "avatar.png")),
        NormalizedEmail = "admin@admin.com",
        EmailConfirmed = true,
        PasswordHash = hasher.HashPassword(null, "QWERTY"),
        SecurityStamp = string.Empty,
        ConcurrencyStamp = Guid.NewGuid().ToString(),
      });

      builder.Entity<IdentityUserRole<Guid>>().HasData(new IdentityUserRole<Guid>
      {
        RoleId = ROLE_ID,
        UserId = ADMIN_ID
      });

      builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });

    }

I think problem is in this part, as user is not assigned in DB to user.我认为问题出在这部分,因为用户没有在数据库中分配给用户。 This table is empty for some reason, but what is actually the problem?由于某种原因,这张表是空的,但实际上是什么问题?

  builder.Entity<IdentityUserRole<Guid>>().HasData(new IdentityUserRole<Guid>
  {
    RoleId = ROLE_ID,
    UserId = ADMIN_ID
  });

  builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });

在此处输入图像描述

ApplicationRole.cs:应用程序角色.cs:

  public class ApplicationRole : IdentityRole<Guid>
  {
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
  }

ApplicationDbContext:应用程序上下文:

  public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid, IdentityUserClaim<Guid>,
  ApplicationUserRole, IdentityUserLogin<Guid>,
  IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
  {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {

    }
        ...
    }

The problem is of course NewGuid() call here ADMIN_ID = Guid.NewGuid();问题当然是NewGuid()在这里调用ADMIN_ID = Guid.NewGuid(); . .

All keys (primary, alternate, foreign) used in Model seed data must be predefined values, that's why they are required even for normally auto generated columns. Model 种子数据中使用的所有键(主键、备用键、外键)必须是预定义的值,这就是即使对于通常自动生成的列也需要它们的原因。 See Data Seeding section of the documentation, and especially Limitations of model seed data :请参阅文档的 数据播种部分,尤其是 model 种子数据的限制

  • The primary key value needs to be specified even if it's usually generated by the database.即使通常由数据库生成,也需要指定主键值。 It will be used to detect data changes between migrations.它将用于检测迁移之间的数据更改。
  • Previously seeded data will be removed if the primary key is changed in any way.如果以任何方式更改主键,则将删除以前播种的数据。

And then接着

Therefore this feature is most useful for static data that's not expected to change outside of migrations and does not depend on anything else in the database, for example ZIP codes.因此,此功能对于 static 数据最有用,这些数据预计不会在迁移之外更改并且不依赖于数据库中的任何其他内容,例如 ZIP 代码。

So the first thing you need to decide is whether your data (default user, role etc.) is appropriate for model data seeding (can these be modified/deleted by the application/end user).因此,您需要确定的第一件事是您的数据(默认用户、角色等)是否适合 model 数据播种(应用程序/最终用户是否可以修改/删除这些数据)。 If the data is not static, then you shouldn't use model data seeding ( HasData ) but some of the other methods mentioned there.如果数据不是 static,那么您不应使用 model 数据播种 ( HasData ),而是使用其中提到的其他一些方法。

And if it is, then simply pregenerate the needed Guids (with some tool or with Guid.NewGuid() ), and then, since C# does not support Guid constants, take their string representation and use Guid constructor with string argument to populate them.如果是,那么只需预先生成所需的 Guid(使用某些工具或使用Guid.NewGuid() ),然后,由于 C# 不支持Guid常量,采用它们的字符串表示并使用带有字符串参数的Guid构造函数来填充它们。 eg例如

Guid ADMIN_ID = new Guid("22ffc532-008e-492d-92b1-e867501f2d54"); // from Guid.NewGuid().ToString() beforehand
Guid ROLE_ID = ADMIN_ID;

or use static readonly fields/properties outside the method in case you need them in other places或在方法之外使用 static 只读字段/属性,以防您在其他地方需要它们

static Guid ADMIN_ID { get; } = new Guid("22ffc532-008e-492d-92b1-e867501f2d54");
static Guid ROLE_ID => ADMIN_ID;

I can't really say where the issue is without running some code but I can show you my implementation and maybe it will ring a bell.如果不运行一些代码,我真的不能说问题出在哪里,但我可以向您展示我的实现,也许它会响起。 This is my version:这是我的版本:

public class SessionDbContext : IdentityDbContext<MachineUserIdentity, IdentityRole<Guid>, Guid>
{
    ...
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Default users
        modelBuilder.ApplyConfiguration(new IdentityUserModelConfig());
        // Default roles
        modelBuilder.ApplyConfiguration(new IdentityRoleModelConfig());
        // User claims
        modelBuilder.ApplyConfiguration(new IdentityUserClaimsModelConfig());
        // Role claims
        modelBuilder.ApplyConfiguration(new IdentityRoleClaimsModelConfig());
        // User roles assignment
        modelBuilder.ApplyConfiguration(new IdentityUserRoleModelConfig());
    }
}

Foreach data table I've created a separate IEntityTypeConfiguration but it shouldn't change anything. Foreach 数据表我创建了一个单独的IEntityTypeConfiguration但它不应该改变任何东西。 Here are the implementations for Users, Roles and UserRoles:以下是用户、角色和用户角色的实现:

IEntityTypeConfiguration<MachineUserIdentity> with MachineUserIdentity inheriting IdentityUser<Guid> : IEntityTypeConfiguration<MachineUserIdentity>MachineUserIdentity继承IdentityUser<Guid>

public void Configure(EntityTypeBuilder<MachineUserIdentity> builder)
    {
        // Seed default roles
        builder.HasData(SeedHelpers.DefaultUsers);
    }

IEntityTypeConfiguration<IdentityRole<Guid>> : IEntityTypeConfiguration<IdentityRole<Guid>>

public void Configure(EntityTypeBuilder<IdentityRole<Guid>> builder)
    {
        // Seed default roles
        builder.HasData(SeedHelpers.GenerateDefaultRoles());
    }

IEntityTypeConfiguration<IdentityUserRole<Guid>> : IEntityTypeConfiguration<IdentityUserRole<Guid>>

public void Configure(EntityTypeBuilder<IdentityUserRole<Guid>> builder)
    {
        // Seed default roles
        builder.HasData(SeedHelpers.GenerateUserRolesAssignment());
    }

Very similar to what you are doing.与您正在做的事情非常相似。 Finally, I have a seeding helper class where I define data like this:最后,我有一个播种助手 class ,我在其中定义如下数据:

internal const string GUID_USER_OPERATOR = "491a282c-3af1-4d45-b6a1-6014b8195744";
internal const string GUID_USER_SERVICE = "337acfd6-fde7-4d5c-9c0b-08fed843a3ed";

internal const string GUID_ROLE_OPERATOR = "d80a2a14-78a7-4e9d-b228-1cd259bd8cd3";
internal const string GUID_ROLE_SERVICE = "69605900-4fb1-4558-a9d4-6bdf3f184819";

// Password do not belong here, move them to a key vault
// This is just for demo purposes
internal const string ID_DEFAULT_ACCOUNT = "ADMIN";
internal const string ID_DEFAULT_PASSWORD = "ADMIN";

internal const string ID_SERVICE_ACCOUNT = "SERVICE";
internal const string ID_SERVICE_PASSWORD = "SERVICE1234";

internal static List<MachineUserIdentity> DefaultUsers = new()
{
    new()
    {
        Id = Guid.Parse(GUID_USER_OPERATOR),
        UserName = ID_DEFAULT_ACCOUNT,
        NormalizedUserName = ID_DEFAULT_ACCOUNT,
        Role = MachineRoleId.Operator.ToString(),
        PasswordHash = hasher.HashPassword(null, ID_DEFAULT_PASSWORD),
    },
    new()
    {
        Id = Guid.Parse(GUID_USER_SERVICE),
        UserName = ID_SERVICE_ACCOUNT,
        NormalizedUserName = ID_SERVICE_ACCOUNT,
        Role = MachineRoleId.Service.ToString(),
        PasswordHash = hasher.HashPassword(null, ID_SERVICE_PASSWORD),
    }
};

internal static IdentityRole<Guid>[] GenerateDefaultRoles()
{
    return new[]
    {
        new(MachineRoleId.Operator.ToString())
        {
            Id = Guid.Parse(GUID_ROLE_OPERATOR),
            Name = MachineRoleId.Operator.ToString(),
            NormalizedName = MachineRoleId.Operator.ToString().ToUpper(),
        },
        new IdentityRole<Guid>
        {
            Id = Guid.Parse(GUID_ROLE_SERVICE),
            Name = MachineRoleId.Service.ToString(),
            NormalizedName = MachineRoleId.Service.ToString().ToUpper(),
        }
    };
}

internal static IdentityUserRole<Guid>[] GenerateUserRolesAssignment()
{
    return new[]
    {
        new()
        {
            RoleId = Guid.Parse(GUID_ROLE_OPERATOR),
            UserId = Guid.Parse(GUID_USER_OPERATOR),
        },
        new IdentityUserRole<Guid>
        {
            RoleId = Guid.Parse(GUID_ROLE_SERVICE),
            UserId = Guid.Parse(GUID_USER_SERVICE),
        }
    };
}

I guess there isn't much difference except that I'm using hard-coded GUIDs (my own choice to have same identifiers on all target machines) and that I do not explicitly declare the key of the UserRole table like you.我想除了我使用硬编码的 GUID(我自己选择在所有目标机器上使用相同的标识符)并且我没有像你一样明确声明 UserRole 表的键之外,没有太大区别。

Hope this helps;)希望这可以帮助;)

Not sure if this will solve your issue but I've noticed that you are assigning new GUID value to ADMIN_ID and then basically passing the same value to ROLE_ID.不确定这是否会解决您的问题,但我注意到您正在为 ADMIN_ID 分配新的 GUID 值,然后基本上将相同的值传递给 ROLE_ID。 Assign a new unique value to ROLE_ID then retry the seed.为 ROLE_ID 分配一个新的唯一值,然后重试种子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 EF Core 2.1.0 中播种管理员用户? - How to seed an Admin user in EF Core 2.1.0? 如何在 ASP.NET Identity 中使用 OnModelCreating 添加管理员用户? - How can I add admin user using OnModelCreating in ASP.NET Identity? 将种子数据放在EF代码优先的OnModelCreating方法中是否可以接受? - Is it acceptable to put seed data in the OnModelCreating method in EF code-first? 如何使用ASP.NET Identity将用户播种到数据库中? - How to seed a user into the database with ASP.NET Identity? 在启用自动生成主键的情况下,如何在 DbContext 的“OnModelCreating”方法期间播种主数据? - How do I seed the master data during "OnModelCreating" method of DbContext in case of auto generated primary key enabled? 身份2的种子数据库 - Seed database for Identity 2 实体框架数据库种子不播种 - Entity Framework database seed doesn't seed Seed()未完全更新数据库 - Seed() not fully updating the database 使用默认超级用户为ASP.NET Core 1.1数据库添加种子 - Seed ASP.NET Core 1.1 database with a default super-user 当引用另一个实体并且它们都没有任何数据时,如何在 OnModelCreating 方法中播种数据? - How can I seed data in the OnModelCreating method when there's a reference to another entity and none of them have any data?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM