Customizing profile information in ASP.NET Identity in VS 2013 templates

Customizing profile information in ASP.NET Identity in VS 2013 templates

Rate This
  • Comments 46

ASP.NET Identity is the new membership system for ASP.NET applications. To learn more about ASP.NET Identity please visit http://blogs.msdn.com/b/webdev/archive/2013/06/27/introducing-asp-net-identity-membership-system-for-asp-net-applications.aspx

One of the mainline features about ASP.NET Identity is to make it easy to add profile information about the user. In the existing ASP.NET Membership system, User and Profile were separate tables and Profile information about the user was retrieved by using the Profile provider. This made it difficult to customize the Profile information and associate it with the User and application data.  ASP.NET Identity makes it really easy to add profile data for your User.

[Update] Adding information on how to store profile information in a different table

[Update] Rick Anderson has a tutorial on asp.net site which also shows how you can add Social Logins to the template as well. Please refer to the tutorial for an entire walkthrough

http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on


This post shows how you can customize profile information about the user when you start with the project templates in Visual Studio 2013. Following are the steps to follow to add profile information about the user.

    • In Visual Studio 2013, create a new ASP.NET MVC application.
      • You can create a Web Forms application and follow the same steps to add profile information.
    • Run the application and register a user
      • You would notice that at the registration screen, the application only asks for a UserName and password. Let’s say that you wanted to add one more field called BirthDate that the users should specify when registering a user.
    • Enable Entity Framework Migrations
      • Migrations are recommended to use when you are modifying the schema of the database or you are changing the Code First Model. ASP.NET Identity uses Entity Framework Code First as an underlying framework. For more information on EF migrations please visit http://msdn.microsoft.com/en-us/data/jj591621
      • Open Package Manager Console by doing Tools – Library Package Manager – Package Manager Console
      • image
      • Type in “Enable-Migrations” and press enter
    • Add properties for the Profile information that you want to store.
      • In our example we will add BirthDate as a property
      • Goto Models\IdentityModels.cs and add the following property to ApplicationUser
public class ApplicationUser : IdentityUser { public DateTime BirthDate { get; set; } }
      • Add using System; at the top of the class.
    • Add-Migrations to modify the database
      • Since we have added a new property to the User, we need to update the database to reflect this change. This is where EF migrations is really useful. You can use migrations to update the database schema by doing the following
      • Goto Package Manager console and do the following
      • Add-Migration "Birthdate"
        • This will add a migration file to your project
      • Update-Database
        • This command will run all the migration files and update the database schema to add a BirthDate column
    • Modify the application to add a BirthDate field
      • The steps here are specific to how you would add a view to MVC, but you can do similar view specific rendering in Web Forms as well
      • Add BirthDate to RegisterViewModel in Models\AccountViewModels.cs
   1:  public class RegisterViewModel { 
   2:  [Required] [Display(Name = "User name")] 
   3:  public string UserName { get; set; }  
   4:   
   5:  [Required] 
   6:  [StringLength(100, ErrorMessage = "The {0} must be 
       at least {2} characters long.", MinimumLength = 6)] 
   7:  [DataType(DataType.Password)] [Display(Name = "Password")] 
   8:  public string Password { get; set; }  [DataType(DataType.Password)]
        [Display(Name = "Confirm password")] 
   9:   
  10:  [Compare("Password", ErrorMessage = 
       "The password and confirmation password do not match.")] 

11: public string ConfirmPassword { get; set; }

public DateTime BirthDate { get; set; } }

        • Add using System; at the top of the class.
      • Update the Register view in Views\Account to add a field to enter BirthDate.
   1:  <div class="form-group">
   2:   @Html.LabelFor(m => m.BirthDate, new { @class = "col-md-2 control-label" }) 
   3:  <div class="col-md-10"> @Html.TextBoxFor(m => m.BirthDate, new { @class = "form-control" }) 
   4:  </div>
   5:   </div>
      • Update the Register Action in AccountController to store the BirthDate as well for the User
var user = new ApplicationUser() { UserName = model.UserName, BirthDate=model.BirthDate };
  • Run the application and register a user again. You will see the BirthDate field as shown below

 

    • image
  • Display the profile infromation on the page
    • When the User Logs in, you can display the profile information by doing the following
    • Get the current logged in UserId, so you can look the user up in ASP.NET Identity system
      • var currentUserId = User.Identity.GetUserId();
    • Instantiate the UserManager in ASP.Identity system so you can look up the user in the system
      • var manager = new UserManager<MyUser>(new UserStore<MyUser>(new MyDbContext()));
    • Get the User object
      • var currentUser = manager.FindById(User.Identity.GetUserId());
    • Get the profile information about the user
      • currentUser.BirthDate

Adding Profile information in a different table.

While this post shows you a way where you can easily add profile properties to the User Table itself. Since ASP.NET Identity uses Entity Framework, you can customize profile information as you would like to. For eg. let’s say that you want to add profile information in a different table rather than the same table then you can do the following to your model

Add a new class to store Profile information

Code Snippet
  1. public class MyUser : IdentityUser
  2.     {
  3.         public virtual MyUserInfo MyUserInfo { get; set; }
  4.     }
  5.  
  6.     public class MyUserInfo{
  7.         public int Id { get; set; }
  8.         public string FirstName { get; set; }
  9.         public string LastName { get; set; }
  10.     }
  11.     public class MyDbContext : IdentityDbContext<MyUser>
  12.     {
  13.         public MyDbContext()
  14.             : base("DefaultConnection")
  15.         {
  16.         }
  17.         public System.Data.Entity.DbSet<MyUserInfo> MyUserInfo { get; set; }
  18.      }

In this case when you run the application you will get the FirstName and LastName in a different table called MyUserInfo

image

Getting Profile information

  • When the User Logs in, you can display the profile information by doing the following
  • Get the current logged in UserId, so you can look the user up in ASP.NET Identity system
    • var currentUserId = User.Identity.GetUserId();
  • Instantiate the UserManager in ASP.Identity system so you can look up the user in the system
    • var manager = new UserManager<MyUser>(new UserStore<MyUser>(new MyDbContext()));
  • Get the User object
    • var currentUser = manager.FindById(User.Identity.GetUserId());
  • Get the profile information about the user
    • currentUser.MyUserInfo.FirstName

Conclusion

This post shows how you can customize the profile information about the user. If you have any questions, please leave comments or reach me at twitter (@rustd)

Leave a Comment
  • Please add 5 and 8 and type the answer here:
  • Post
  • @Bill What did you figure out on that line of code? I can't figure out what MyUser is, and I am also passing in my DbContext class (the one made that inherits DbContext) and I am getting the red sqwiggly line of death..

  • How do we we add "scopes" to the Identity providers that  come out of the Box with VS2013 RTM.  Especially, MicrosoftAccount,  Facebook and Google. I saw your example on stackoverflow but it focuses only on Facebook; Is there a general approach for doing this?

  • How can i add a new role and add a user to this role on new identity system?

  • Hi Pranav, did your first migration included the membership tables? If it did, have you tried to apply that migration to a SQL Server CE database (assuming you started with a SQL Server database)?

    I have and I always get the error "Unable to update database to match the current model because there are pending changes and automatic migration is disabled." It seems that the keys on the membership tables are not defined the same on SQL Server CE and SQL Server.

    Do you know of any way to resolve this? I use SQL CE during development, because its faster, so I can stop using it for the moment. But I rather keep using it.

  • Can we also customize the profile information about the user in a project of type asp .net web api? I get the following exception when trying to register a new user:  

    "{"Message":"An error has occurred.","ExceptionMessage":"Mapping and metadata information could not be found for EntityType 'CustomIdentityWebAPI.Models.ApplicationUser'"

    Here is the code:

    1. Created a new user that inherits from IdentityUser

    public class ApplicationUser: IdentityUser

       {

           public string Email { get; set; }

       }

    2.  This is my dbcontext

    public class ApplicationDbContext: IdentityDbContext<ApplicationUser>

       {

           public ApplicationDbContext()

               : base("DefaultConnection")

           {

           }

       }

    3. I also modified Register in AccountController to user my custom user

    // POST api/Account/Register

           [AllowAnonymous]

           [Route("Register")]

           public async Task<IHttpActionResult> Register(RegisterBindingModel model)

           {

               if (!ModelState.IsValid)

               {

                   return BadRequest(ModelState);

               }

               var user = new ApplicationUser

               {

                   UserName = model.UserName,

                   Email = model.Email

               };

               IdentityResult result = await UserManager.CreateAsync(user, model.Password);

               IHttpActionResult errorResult = GetErrorResult(result);

               if (errorResult != null)

               {

                   return errorResult;

               }

               return Ok();

           }

    4. Enabled migration and updated the database the AspNetUsers table updated and has two knew coums: Email and Discriminator

    But when I try to register a new ApplicationUser a get the following exception in Register

    {"Message":"An error has occurred.","ExceptionMessage":"Mapping and metadata information could not be found for EntityType 'CustomIdentityWebAPI.Models.ApplicationUser'.","ExceptionType":"System.InvalidOperationException","StackTrace":"   at System.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()"}

  • I love all of these examples, but to be quite frank, they're all in MVC.  What about the rest of us?  Too often we're left to try to recode all of these into web forms.  Please provide examples in web forms too if you're going to supply these.  It would be really appreciated.  This one in particular isn't too difficult to translate, but still...

  • Not work for me.

    After trying Add-Migration statement, I got a class with Up() and Down() methods. In Up() methods, there are some CreateTable calls.

    And when I ran update-database statement, error occurred because tables are already existed

  • Hi, how would you update the birthdate once it already exists in the db? say if the user was to initial add it in error. It would be interesting to see the code that would replace the below 2 lines.

    Edit/Update action result for BirthDate below.

    __________________________________________

    //

           // POST: /Account/BirthDate

           [HttpPost]

           [ValidateAntiForgeryToken]

           public ActionResult BirthDate(BirthDateViewModel viewModel)

           {

               if (ModelState.IsValid)

               {

                   // Update BirthDate in database

                   using (ApplicationDbContext db = new ApplicationDbContext())

                   {

    // What goes below to save updated birthdate in database

                       db.Entry(AspNetUsers).State = EntityState.Modified;

                       db.SaveChanges();

    // What goes above to save updated birthdate in database

                       TempData["message"] = string.Format("BirthDate saved.");

                       return RedirectToAction("BirthDate", "Account");

                   }

               }

               return View(viewModel);

           }

  • Good stuff but it leaves me with more questions!

    I successfully added the AspnetUser tables to my existing DB, which is what I wanted.

    But I cannot add further registration info to my SiteUser table. I get the following message;

    The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database.

    I don't want code first as I already have a corporate DB that we wish to use. So I tried adding an ADO.NET Data Entity Model - Good stuff, but how do I link it in?

    You have offered getting profile information, but you haven't given me a clue as to putting the info! I wish Bill had answered you question re code, as that is my question too.

    Also can you provide a sample code of where I would place var currentUserId = User.Identity.GetUserId(); so that the id is added to my SiteUser table. My relationships are already in the db, but I need to see where it should go in the existing code.

    Ah, the joy of bleeding edge... If I don't figure this out soon my boss is nagging me to use SimpleMembership! UGH!!!

  • This is great.  One question however.  How can you use the Seed method to AddOrUpdate this new field?

  • Hello,

    This may be obvious to everyone else, but it was not to me. So, if you decide to create a second table for additional user information as the second part of this posts shows, and you want those fields for that new table to show on the Register screen: the one thing that is a little different from what they show you is that you have to create a new object for that new table, and then add that object as a parameter when creating the new ApplicationUser object. That way it creates a new row in your second table too. Here is my code that should make what I mean clear:  (in the code below my second table was named Person and it had email, phone, and such fields. It also assumes you followed the non-new-table example for birthdate.)

    var person = new Person() { Email = model.Email, Forename = model.Forename, OfficeLocation = model.OfficeLocation, Phone = model.Phone, Surname = model.Surname };

                   var user = new ApplicationUser() { UserName = model.UserName, BirthDate=model.BirthDate,  Person = person};

  • @Alexa You need to map the Identity Model to your custom database schema so that you can add more profile information. Please send me an email at pranav.rastogi@microsoft.com and we can help you get unblocked. We are in the process of documenting this...

  • Thanks for this tutorial. I'm getting the same error as a few other people here. I tried to add Email as a property to the default user table. I added the column through migrations successfully and there aren't any errors before running. Here is the error message I'm getting when I try to register a new user:

    Mapping and metadata information could not be found for EntityType 'flat_file.Models.ApplicationUser'.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.InvalidOperationException: Mapping and metadata information could not be found for EntityType 'flat_file.Models.ApplicationUser'.

    Here is the code from the register controller:

    ...

    var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email};

    var result = await IdentityManager.Users.CreateLocalUserAsync(user, model.Password);

    if (result.Success)

    {

    ....

    This is the code from IdentityModel.cs:

    using Microsoft.AspNet.Identity.EntityFramework;

    using System;

    namespace flat_file.Models

    {

       public class ApplicationUser : User

       {

           public string Email { get; set; }

       }

       //I believe the issue is here

       public class ApplicationDbContext : IdentityDbContextWithCustomUser<ApplicationUser>

       {

       }

    }

    Any help would greatly be appreciated!

  • Hi Pranav, great tutorial.

    I never liked to user Membership because I don't really want to add 10 tables to my database in order to get the Login information. All those "schema versions, applications, paths, personalizations, ..." are good for wizard type applications and not for specific optimized websites.

    So I'm trying to understand if it's possible to enhance the Identity and have it work with my User Table. If so, do you know what should I implement? Is that a matter of overriding (or extending) the UserManager.FindAsync and UM.CreateIdentityAsync and related needed methods ??

    Any thoughts on that?

    Thanks in advanced.

  • Hi! How do you mock (or stub) User.Identity.GetUserId() for a unit test? GetUserId() is an extension, so I can't mock it directly. Do you have alternatives?

Page 2 of 4 (46 items) 1234