问题
Brand new to ASP.Net Core. Having to create an asp.net core 2.2 project with Identity (and have users seeded).
I can't find any documentation on how to do this exactly.
I was able to find the code to create Identity Roles (compiles anyway, haven't gotten to where I can run it yet:
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));
}
}
}
Now, I need to create some users. But the with the weird microsoft syntax to do that I can't find (been googling for 2 days).
Here's what does not work:
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");
}
The error I see is:
Argument1: cannot convert from 'Microsoft.AspNetCore.Identity.IdentityUser' to 'ApplicationDbContext'
Does that mean? Obviously, I don't have the right userManager. But, how do I get the right one that takes a user as the 1st parameter and a string (password) for the 2nd?
In addition, the examples that come up in Google searches have an ApplicationUser
object that I do not have (and don't need?). Not defined in the examples as to how I get it.
Owen
OK. Got past syntax error, but now I'm getting a runtime error:
NullReferenceException: Object reference not set to an instance of an object. on the call to CreateAsync. Here's the new code:
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");
}
Going to be looking into what the other parameters are to the create userManager and how to get them from the serviceProvider?
--Owen
Figured out how to do it. The key was finding the correct serviceprovider to pass in and the right syntax for creating the userManager. The other answers I've found through google all replace the IdentityUser
with their own ApplicationUser
that was muddying the water. Here's the working function (hope this helps someone):
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");
}
回答1:
Your main issue seems to be dependency injection. Have a look at this link for more information. As long as you inject your DbContext
and UserManager
in the right way and the rest of the code should be fine.
Here is an example. You can set up a separate service for seeding to ensure you decouple your code from the rest.
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);
}
}
}
You then have to add this class as a scoped (since your DbContext
is scoped) by using services.AddScoped<UserSeeder>()
in your startup. You can now simply inject your UserSeeder
in any service (except singletons) and call your UserSeeder
function. For instance, You can inject UserSeeder
in the home controller and call it index action. This way the seeding is checked and added initially. However, this will only work IF you go to the home page first. Alternatively, you can set up a middleware like this in your startup class:
app.Use(async (context, next) => {
await context.RequestServices.GetService<UserSeeder>().SeedAsync();
await next();
});
Note that both of these ways, you are calling the database every time. You can plan on where to place it. You can also make sure this is only called once with the help of a boolean (could be in a singleton). But note that this would only run on application startup.
回答2:
Here's how I seed my Admin user (learned from EF Core in Action book):
This is the User
class:
public class User : IdentityUser<long>
{
//add your extra properties and relations
}
The long
type specifies the primary key type. If you use the default IdentityUser
class it's going to be string (uniqueidentifier in SQL).
This is the Role
class:
public class Role : IdentityRole<long>
{
public static string Admin = "Admin";
}
It can be empty, I use static strings to avoid magic strings in my code.
This is the DbContext
:
public class ApplicationDbContext : IdentityDbContext<User, Role, long>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
//your DbSets and configurations
//...
}
If you're going to use Identity, you need to use IdentityDbContext
and specify your custom User
and Role
class and the type of primary key you're using.
This code adds Identity to the program:
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();
//...
}
This is an extension method to seed data:
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;
}
}
And finally use it in your Program.cs
:
CreateWebHostBuilder(args)
.Build()
.SeedAdminUser()
.Run();
来源:https://stackoverflow.com/questions/53686409/asp-net-core-2-2-create-identityuser