Creating the Family Page–Part 3, User Accounts–expanding the IdentyUser

The next couple of posts we will talk about user accounts. This time we will look at expanding the information stored on the user account.

First we will take a look at how user account data is stored. As mentioned earlier, the standard setup uses localdb (this simply means that the database is kept in a simple file, that can be uploaded to any web hotel if so desired).

The database file does not exist until you put something in it. To do so, run the application and register a user with username and password. Then stop the application. In the solution explorer, click on App_Data, and then click on the icon “Show All Files”.

image

Double-click the database file to open the file in the server explorer.

image

As can be seen, I have created a user with the username ‘Michael’ and the password is not stored in clear text. This is the only one of the tables, that are used in the standard setup. The other tables are used if you want to assign individual roles to the users, or you want to let the user be able to log in with an external authentication provider (like Google, Twiiter or Facebook).

For this simple page we will not need other means to log in. We will need roles, and that is the subject of one of the next posts.

We will need to expand the user data a little. Besides user name and password, we will also need real name and e-mail address. This is data that is reasonable to store along with the login information. If you also want to store more data about the user (like address, homepage, spouse or whatever) you should do it in another table and handle that someplace else.

The work for today:

  • Enable Entity Framework code first migrations
  • Expand the IdentityUser
  • Update the views to utilize the extra information.

Enable Entity Framework code first migrations

First lets talk about architecture: The template Microsoft supplies with Visual Studio 2013 is meant to help with a quick start on your project. There is no separate DAL project. If you want to, you can create a new project and install Entity Framework in that project instead and move the data files from the main project. (To install EntityFramework in another project run Install-Package EntityFramework -ProjectName from the PackageManager console)

For this simple web page we will keep it all in one project.

In order to enable migrations enter “Enable-Migrations” in the PackageManager console:

image

This results in some new files in the solution explorer:

image

This will enable EntityFramework to create a new database with the same tables that we just looked at in the start of this post.

Under models we have the file IdentityModels.cs with the following content:

using Microsoft.AspNet.Identity.EntityFramework;

namespace TheFamilyPage.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
    }

    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

 

We can see that the ApplicationUser has an empty inheritance from the IdentityUser, which just gives us a user with username and password.

The IdentityUser is implemented like this:

 

namespace Microsoft.AspNet.Identity.EntityFramework
{
  public class IdentityUser : IUser
  {
    public virtual string Id { get; set; }

    public virtual string UserName { get; set; }

    public virtual string PasswordHash { get; set; }

    public virtual string SecurityStamp { get; set; }

    public virtual ICollection<IdentityUserRole> Roles { get; private set; }

    public virtual ICollection<IdentityUserClaim> Claims { get; private set; }

    public virtual ICollection<IdentityUserLogin> Logins { get; private set; }

    public IdentityUser()
    {
      this.Id = Guid.NewGuid().ToString();
      this.Claims = (ICollection<IdentityUserClaim>) new List<IdentityUserClaim>();
      this.Roles = (ICollection<IdentityUserRole>) new List<IdentityUserRole>();
      this.Logins = (ICollection<IdentityUserLogin>) new List<IdentityUserLogin>();
    }

    public IdentityUser(string userName)
      : this()
    {
      this.UserName = userName;
    }
  }
}

 

Pretty simple – now we want to expand on that.

Expand the IdentityUser

In order to expand the IdentityUser we add some extra fields to the ApplicationUser class.

    public class ApplicationUser : IdentityUser
    {
      public string Name { get; set; }
      public string Email { get; set; }
    }

 

Now our application user also has a Name and an Email. now we need to add those fields to the database also. In the PackageManger Console write “Add-Migration UserData”

This creates some new migration code:

image

The file contains code to update the database, but also to roll back the database if needed:

namespace TheFamilyPage.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class UserData : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.AspNetUsers", "Name", c => c.String());
            AddColumn("dbo.AspNetUsers", "Email", c => c.String());
        }
        
        public override void Down()
        {
            DropColumn("dbo.AspNetUsers", "Email");
            DropColumn("dbo.AspNetUsers", "Name");
        }
    }
}

In order to do the actual update of the database, type “Update-Database” in the PackageManager console.

Looking in the AspNetUsers table in the database we can se that the new fields now exist in the database:

image

Next we need to use the new data:

Update the views

First we need to update the Register AccountViewModel with our new data:

public class RegisterViewModel
  {
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }
  }

 

We also need to change the AccountControllers Register action:

[HttpPost] 
[AllowAnonymous] 
[ValidateAntiForgeryToken] 
public async Task Register(RegisterViewModel model) 
{ 
  if (ModelState.IsValid) 
  { 
    var user = new ApplicationUser() 
    { 
      UserName = model.UserName,
      Name = model.Name, Email = model.Email
    }; 
    var result = await UserManager.CreateAsync(user, model.Password); 
    if (result.Succeeded) 
    { 
      await SignInAsync(user, isPersistent: false); 
      return RedirectToAction("Index", "Home"); 
    } 
    else 
    { 
      AddErrors(result); 
    } 
  } 
  // If we got this far, something failed, redisplay form 
  return View(model); 
}

 

We also add Name and Email to the RegisterView

@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>Create a new account.</h4>
    <hr />
    @Html.ValidationSummary()
    <div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Name, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Register" />
        </div>
    </div>
}

Now I try to run the page and register a new user. When I look at the data table I can see  that the name and e-mail is stored for my new user.

image

I can also se that my first registered user doesn’t have name or e-mail set. I have no way to remedy that yet.

Conclusion

We now have the ability to register a user with name and e-mail, and store the result in the database. We still want to add a couple of things to our account handling:

  • We want to be able to change our name and e-mail.
  • We want to clean up the views and controllers, so we don’t have code we don’t need.
  • We need to handle that users might forget their password
  • We need to add Roles, for an administrator, an editor (that also can edit other users wish-lists) and a normal user, who can only edit his/her own data.

So that is the plan for the next couple of posts.

Advertisements

About Lund

Owner of iCodeIT, a software consulting company. I am primarily working the .NET development and architecture.
This entry was posted in C#, HTML5, Web and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s