Revising the DbSeeder

Now that our ApplicationUser model class is inheriting the IdentityUser base class, we most likely broke the seeding mechanism we set up back in Chapter 4, Data Model with Entity Framework Core. However, it's not a big deal; we can take the chance to create some sample roles, since we can now make good use of them.

The first thing we need to do is to provide our DbSeeder class with a UserManager and a RoleManager, as they are the required Microsoft.AspNetCore.Identity handler classes to properly work with users and roles. We can inject them using DI within the service scope we defined in the Startup.cs file back in Chapter 4, Data Model with Entity Framework Core, and then pass them to the DbSeed.Seed() method, just like we did with the DbContext.

These are the changes to apply to the Startup.cs file (new and changed parts are highlighted):

[...]

// Create a service scope to get an ApplicationDbContext instance using DI
using (var serviceScope =
app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext =
serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var roleManager =
serviceScope.ServiceProvider.GetService<RoleManager<IdentityRole>>
();

var userManager =
serviceScope.ServiceProvider.GetService<UserManager<ApplicationUser>>
();

// Create the Db if it doesn't exist and applies any pending
migration.
dbContext.Database.Migrate();
// Seed the Db.
DbSeeder.Seed(dbContext, roleManager, userManager);
}

[...]

Once done, we can open our /Data/DbSeeder.cs file and update the Seed() method accordingly:

[...]

public static Seed(
ApplicationDbContext dbContext,
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager
)
{

// Create default Users (if there are none)
if (!dbContext.Users.Any())
{
CreateUsers(dbContext, roleManager, userManager)
.GetAwaiter()
.GetResult();
}

// Create default Quizzes (if there are none) together with their
set of Q&A
if (!dbContext.Quizzes.Any()) createQuizzes(dbContext);
}

[...]

Other than the expected references to the RoleManager and UserManager, we can easily see that we did something else. Specifically, we changed the way we're executing the CreateUsers() method; instead of simply invoking it, we're now using GetAwaiter() to get an awaiter object that will await the completion of its task, followed by a GetResult() that will end the wait and return the resulting value (which is null in our case) and return to the main execution context.

This is a rather common way to deal with async tasks from a synchronous context; that's good to know, yet why did we do? Are we planning to change CreateUsers() into an async method returning a Task?

The answer is yes, that's precisely what we're about to do. The reason is simple--since most of the roleManager and userManager method are asynchronous, the best way to invoke them would be from an async method. This leaves us with two possible routes: go async all the way up to the Configure() method within the Startup.cs file, or block it somehow. We went for the latter options, along with some important caveats that we'll briefly introduce at the end of this section.

The next step involves upgrading the CreateUsers method:

[...]

private static async Task CreateUsers(
ApplicationDbContext dbContext,
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager)
{
// local variables
DateTime createdDate = new DateTime(2016, 03, 01, 12, 30, 00);
DateTime lastModifiedDate = DateTime.Now;

string role_Administrator = "Administrator";
string role_RegisteredUser = "RegisteredUser";

//Create Roles (if they doesn't exist yet)
if (!await roleManager.RoleExistsAsync(role_Administrator))
{
await roleManager.CreateAsync(new
IdentityRole(role_Administrator));

}
if (!await roleManager.RoleExistsAsync(role_RegisteredUser))
{
await roleManager.CreateAsync(new
IdentityRole(role_RegisteredUser));

}

// Create the "Admin" ApplicationUser account
var user_Admin = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Admin",
Email = "[email protected]",
CreatedDate = createdDate,
LastModifiedDate = lastModifiedDate
};
// Insert "Admin" into the Database and assign the "Administrator"
and "RegisteredUser" roles to him.
if (await userManager.FindByNameAsync(user_Admin.UserName) == null)
{
await userManager.CreateAsync(user_Admin, "Pass4Admin");
await userManager.AddToRoleAsync(user_Admin,
role_RegisteredUser);

await userManager.AddToRoleAsync(user_Admin,
role_Administrator);

// Remove Lockout and E-Mail confirmation.
user_Admin.EmailConfirmed = true;
user_Admin.LockoutEnabled = false;
}

#if DEBUG
// Create some sample registered user accounts
var user_Ryan = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Ryan",
Email = "[email protected]",
CreatedDate = createdDate,
LastModifiedDate = lastModifiedDate
};

var user_Solice = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Solice",
Email = "[email protected]",
CreatedDate = createdDate,
LastModifiedDate = lastModifiedDate
};

var user_Vodan = new ApplicationUser()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Vodan",
Email = "[email protected]",
CreatedDate = createdDate,
LastModifiedDate = lastModifiedDate
};

// Insert sample registered users into the Database and also assign
the "Registered" role to him.

if (await userManager.FindByNameAsync(user_Ryan.UserName) == null)
{
await userManager.CreateAsync(user_Ryan, "Pass4Ryan");
await userManager.AddToRoleAsync(user_Ryan,
role_RegisteredUser);

// Remove Lockout and E-Mail confirmation.
user_Ryan.EmailConfirmed = true;
user_Ryan.LockoutEnabled = false;
}
if (await userManager.FindByNameAsync(user_Solice.UserName) ==
null)

{
await userManager.CreateAsync(user_Solice, "Pass4Solice");
await userManager.AddToRoleAsync(user_Solice,
role_RegisteredUser);

// Remove Lockout and E-Mail confirmation.
user_Solice.EmailConfirmed = true;
user_Solice.LockoutEnabled = false;
}
if (await userManager.FindByNameAsync(user_Vodan.UserName) == null)
{
await userManager.CreateAsync(user_Vodan, "Pass4Vodan");
await userManager.AddToRoleAsync(user_Vodan,
role_RegisteredUser);

// Remove Lockout and E-Mail confirmation.
user_Vodan.EmailConfirmed = true;
user_Vodan.LockoutEnabled = false;
}

#endif
await dbContext.SaveChangesAsync();
}

[...]

As we can see, we made some relevant changes here:

  • As expected, we added the async modifier to the method signature and changed the return type from void to Task; we already know the reason, and we'll tell more about that in a short while, so let's go ahead for now.
  • We used the newly-added roleManager object instance to create two sample roles: Administrator and RegisteredUser.
  • We replaced the existing DbContext.Add and DbContext.AddRange method calls with those provided by the userManager object instance; this allowed us to specify a password that will be automatically hashed.
  • We removed the programmatic Id</kbd> Guid assignment for each user, as the Guid will be autogenerated by the userManager.CreateUserAsync() method, and replaced it with a new SecurityStamp property, also accepting a Guid. This is one of the many properties shipped by the IdentityUser base class, and it's required when creating a new user.
The Administrator and RegisteredUser roles we just implemented here will be the core of our authorization mechanism; all of our users will be assigned to at least one of them. Note how we assigned both of them to the admin user, to make him be able to do everything a standard user can do, plus more: all the other users only have the latter, so they'll be unable to perform any administrative-level task--as long as they're not provided with the Administrator role.

With this, we're done updating our project's classes. Before going further, it might be wise to issue a whole project rebuild to ensure that we're not getting build errors within our code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.117.191