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 = "[email protected]",
Email = "[email protected]"
};
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 = "[email protected]",
Email = "[email protected]"
};
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 = "[email protected]",
Email = "[email protected]"
};
var result = await userManager.CreateAsync(user, "password");
result = await userManager.AddToRoleAsync(user, "Administrator");
}
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 = "[email protected]";
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.