【问题标题】:ASP.NET Core 2.2 Create IdentityUserASP.NET Core 2.2 创建 IdentityUser
【发布时间】:2019-05-10 05:11:34
【问题描述】:

全新的 ASP.Net Core。必须使用 Identity 创建一个 asp.net core 2.2 项目(并让用户播种)。

我找不到任何关于如何准确执行此操作的文档。

我能够找到创建身份角色的代码(无论如何编译,还没有到可以运行它的地方:

  private static async Task CreateUserTypes(ApplicationDbContext authContext, IServiceProvider serviceProvider)
  {
     var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
     string[] roleNames = { "Administrator", "Data Manager", "Interviewer", "Respondent" };
     IdentityResult roleResult;
     foreach (var roleName in roleNames)
     {
        var roleExist = await RoleManager.RoleExistsAsync(roleName);
        if (!roleExist)
        {
           roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
        }
     }
  }

现在,我需要创建一些用户。但是我找不到奇怪的微软语法(谷歌搜索了 2 天)。

的作用如下:

   private static async Task CreateRootUser(Models.CensORContext context, ApplicationDbContext authContext, IServiceProvider serviceProvider)
  {
     //Create the root ADMIN user for all things admin.
     UserManager<ApplicationDbContext> userManager = serviceProvider.GetRequiredService<UserManager<ApplicationDbContext>>();

     IdentityUser user = new IdentityUser()
     {
        UserName = "admin@admin.admin",
        Email = "admin@admin.admin"
     };

     var NewAdmin = await userManager.CreateAsync(user, "password");
  }

我看到的错误是:

Argument1:无法从“Microsoft.AspNetCore.Identity.IdentityUser”转换为“ApplicationDbContext”

这是什么意思?显然,我没有合适的 userManager。但是,我如何获得正确的,它将用户作为第一个参数,将字符串(密码)作为第二个参数?

此外,Google 搜索中出现的示例有一个我没有(也不需要?)的 ApplicationUser 对象。示例中没有定义我如何获得它。

欧文

好的。过去的语法错误,但现在我遇到了运行时错误:

NullReferenceException:对象引用未设置为对象的实例。在调用 CreateAsync 时。这是新代码:

private static async Task CreateRootUser(Models.CensORContext context, ApplicationDbContext authContext, IServiceProvider serviceProvider)
{
     //Create the root ADMIN user for all things admin.         
     var userStore = new UserStore<IdentityUser>(authContext);
     UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(userStore, null, null, null, null, null, null, serviceProvider, null);
     // = serviceProvider.GetRequiredService<UserManager<ApplicationDbContext>>();

     IdentityUser user = new IdentityUser()
     {
        UserName = "admin@admin.admin",
        Email = "admin@admin.admin"
     };

     var result = await userManager.CreateAsync(user, "password");
}

要研究创建 userManager 的其他参数是什么以及如何从 serviceProvider 获取它们?

--欧文

想出了如何去做。关键是找到正确的服务提供者来传递创建 userManager 的正确语法。我通过谷歌找到的其他答案都用他们自己的ApplicationUser替换了IdentityUser,这使水变得浑浊。这是工作功能(希望对某人有所帮助):

  private static async Task CreateRootUser(Models.CensORContext context, ApplicationDbContext authContext, IServiceProvider serviceProvider)
  {
     //Create the root ADMIN user for all things admin.         
     var userStore = new UserStore<IdentityUser>(authContext);
     UserManager<IdentityUser> userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();
     //new UserManager<IdentityUser>(userStore, null, null, null, null, null, null, serviceProvider, null);
     // = serviceProvider.GetRequiredService<UserManager<ApplicationDbContext>>();

     IdentityUser user = new IdentityUser()
     {
        UserName = "admin@admin.admin",
        Email = "admin@admin.admin"
     };

     var result = await userManager.CreateAsync(user, "password");
     result = await userManager.AddToRoleAsync(user, "Administrator");
  }

【问题讨论】:

  • 我理解尝试让代码正常工作时的挫败感,尤其是在截止日期紧迫的情况下。我也理解那种大声喊叫什么的诱惑。我们都曾在某些时候去过那里。虽然您的问题确实传达了您的编码问题,但在发布问题时请尽量保持冷静。这样一来,所有人都更加专注于您的编程问题。
  • 您是否使用个人用户的默认模板创建了新项目?
  • 我建议您添加最后一段,这似乎是工作代码作为您的答案并将其从问题中删除
  • 看看这个docs.microsoft.com/en-us/aspnet/core/fundamentals/…。这似乎是您真正的问题所在,而不是身份。这将有助于更好地理解这个概念。
  • stackOverflow 不会让我发布答案(没有足够的声誉点) - 所以我把我的工作代码放在答案中。我敢肯定还有更好的方法来做到这一点......谢谢大家的cmets和乐于助人。我会努力减轻我的挫败感 - 当周围没有人时很容易大喊大叫......谢谢。

标签: asp.net-core identity seeding asp.net-core-identity usermanager


【解决方案1】:

以下是我为我的管理员用户设置种子的方法(从EF Core in Action 一书中学习):

这是User 类:

public class User : IdentityUser<long>
{
    //add your extra properties and relations
}

long 类型指定主键类型。如果您使用默认的 IdentityUser 类,它将是字符串(SQL 中的唯一标识符)。

这是Role 类:

public class Role : IdentityRole<long>
{
    public static string Admin = "Admin";
}

可以为空,我使用静态字符串来避免我的代码中出现魔术字符串。

这是DbContext

public class ApplicationDbContext : IdentityDbContext<User, Role, long>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    { }

    //your DbSets and configurations
    //...
}

如果您要使用 Identity,您需要使用 IdentityDbContext 并指定您的自定义 UserRole 类以及您正在使用的主键类型。

此代码将身份添加到程序中:

public void ConfigureServices(IServiceCollection services)
{
    //...

    services.AddIdentity<User, Role>(options =>
        {
            //you can configure your password and user policy here
            //for example:
            options.Password.RequireDigit = false;
        })
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    //...
}

这是种子数据的扩展方法:

public static class SeedData
{
    public static IWebHost SeedAdminUser(this IWebHost webHost)
    {
        using (var scope = webHost.Services.CreateScope())
        {
            try
            {
                var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
                context.Database.EnsureCreated();

                var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
                var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<Role>>();

                if (!userManager.Users.Any(u => u.Email == "admin@domain.com"))
                {
                    roleManager.CreateAsync(new Role()
                    {
                        Name = Role.Admin
                    })
                    .Wait();

                    userManager.CreateAsync(new User
                    {
                        UserName = "Admin",
                        Email = "admin@domain.com"
                    }, "secret")
                    .Wait();

                    userManager.AddToRoleAsync(userManager.FindByEmailAsync("admin@domain.com").Result, Role.Admin).Wait();
                }                    
            }
            catch (Exception ex)
            {
                var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred while seeding user.");
                //throw;
            }
        }

        return webHost;
    }
}

最后在你的Program.cs中使用它:

CreateWebHostBuilder(args)
    .Build()
    .SeedAdminUser()
    .Run();

【讨论】:

  • 这是一个很好的方法并且信息量很大,但是有几个建议。首先,不要为每个服务使用GetRequiredService,如果您创建一个单独的类并将其用作服务,它会更干净、更灵活。其次,您可以在启动时在 Configure 函数上运行它,而不是创建扩展并在 program.cs 上运行(代码更多的是启动的工作)。我还发现前三组代码与问题无关
  • Configure 方法中的代码将为每个请求运行。在应用程序启动期间,种子数据应该只发生一次。
  • 不完全是。 Configure 方法只被调用一次。每个请求都会调用函数中创建的中间件。我说的是在配置中调用SeedAdminUser 而不是创建中间件。
【解决方案2】:

您的主要问题似乎是依赖注入。查看this link 了解更多信息。只要您以正确的方式注入DbContextUserManager,其余代码应该没问题。

这是一个例子。您可以为播种设置单独的服务,以确保您将代码与其他代码分离。

public class UserSeeder
{
    private readonly UserManager<IdentityUser> userManager;
    private readonly ApplicationDbContext context;

    public UserSeeder(UserManager<IdentityUser> userManager, ApplicationDbContext context)
    {
        this.userManager = userManager;
        this.context = context;
    }

    public async Task `()
    {
        string username = "admin@admin.admin";
        var users = context.Users;
        if (!context.Users.Any(u => u.UserName == username))
        {
            var done = await userManager.CreateAsync(new IdentityUser
            {
                UserName = username,
                Email = username
            }, username);
        }
    }

}

然后,您必须在启动时使用 services.AddScoped&lt;UserSeeder&gt;() 将此类添加为范围(因为您的 DbContext 是范围)。您现在可以简单地将您的UserSeeder 注入任何服务(单例除外)并调用您的UserSeeder 函数。例如,您可以在主控制器中注入UserSeeder 并将其称为索引操作。通过这种方式,最初会检查并添加播种。但是,这仅在您先转到主页时才有效。或者,您可以在启动类中设置这样的中间件:

app.Use(async (context, next) => {
    await context.RequestServices.GetService<UserSeeder>().SeedAsync();
    await next();
});

请注意,这两种方式都是每次都调用数据库。您可以计划放置它的位置。您还可以确保仅在布尔值的帮助下调用一次(可以在单例中)。但请注意,这只会在应用程序启动时运行。

【讨论】:

  • 如何“在家庭控制器中注入 UserSeeder”? -或者- 我应该把“app.Use(....”放在哪里,以便我有一个上下文等...?
  • 要注入控制器或任何服务,您需要将其作为构造函数参数public HomeController(UserSeeder userSeeder) 传递。基本上,就像我将UserManager&lt;IdentityUser&gt; 注入控制器中的UserSeeder 一样。 app.Use( 在您的 Startup 类中开始您的 Confugure 函数。
  • Ahhh.... 这种注入非常强大,具有我习惯的不同语法。您是否有关于博客/博客/课程/等的建议,以了解如何使用它(不一定是它如何工作或如何构建一个)?
猜你喜欢
  • 2019-05-13
  • 2017-09-20
  • 1970-01-01
  • 2018-04-25
  • 1970-01-01
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 2020-07-11
相关资源
最近更新 更多