How to change identifier type in Microsoft.AspNet.Identity.EntityFramework.IdentityUser

(ASP.NET MVC 5, EF6, VS2013)

I am trying to figure out how to change the type of the "Id" field from a string to an int in a type:

Microsoft.AspNet.Identity.EntityFramework.IdentityUser 

so that new user accounts are associated with an integer identifier, and not with a GUID. But it looks like it will be harder than just adding a new Id property with type int in my derived user class. Take a look at this method signature:

(from the Microsoft.AspNet.Identity.Core.dll assembly)

 public class UserManager<TUser> : IDisposable where TUser : global::Microsoft.AspNet.Identity.IUser { ... public virtual Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login); ... } 

So it seems that there are other methods baked into the ASP.NET authentication framework for which userId is a string. Do I need to override these classes?

Explanation of why I do not want to store GUIDs for identifiers in the user table:

. There will be other tables that associate data with the user table through a foreign key. (When users save content on the site.) I see no reason to use a wider type of field and spend extra database space without obvious benefits. (I know there are other reports about using GUIDs vs int ids, but it seems like many people think that int ids are faster and use less space, which still leaves me wondering.)

- I plan to set a calm endpoint so that users can receive data about a specific user. I think:

 /users/123/name 

cleaner than

 /users/{af54c891-69ba-4ddf-8cb6-00d368e58d77}/name 

Does anyone know why the ASP.NET team decided to implement identifiers this way? I do not suspect that I am trying to change this to an int type? (Perhaps there are advantages that I am missing.)

Thank...

-Ben

+66
asp.net-mvc entity-framework asp.net-mvc-5 asp.net-identity owin
Oct 23 '13 at 22:23
source share
6 answers

So, if you want to set the identifiers, you need to create your own POCO IUser class and implement your IUserStore for your custom IUser class in RTM version 1.0.

This is something that we did not have time for support, but now I learn it easily (ier) in 1.1. Hope something will be available in nightly builds soon.

Updated with example 1.1-alpha1: How to get night buildings

If you upgrade to the last nightly bits, you can try the new 1.1-alpha1 apis, which should make it easier now: here, what Guides include instead of lines should look, for example,

  public class GuidRole : IdentityRole<Guid, GuidUserRole> { public GuidRole() { Id = Guid.NewGuid(); } public GuidRole(string name) : this() { Name = name; } } public class GuidUserRole : IdentityUserRole<Guid> { } public class GuidUserClaim : IdentityUserClaim<Guid> { } public class GuidUserLogin : IdentityUserLogin<Guid> { } public class GuidUser : IdentityUser<Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> { public GuidUser() { Id = Guid.NewGuid(); } public GuidUser(string name) : this() { UserName = name; } } private class GuidUserContext : IdentityDbContext<GuidUser, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> { } private class GuidUserStore : UserStore<GuidUser, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim> { public GuidUserStore(DbContext context) : base(context) { } } private class GuidRoleStore : RoleStore<GuidRole, Guid, GuidUserRole> { public GuidRoleStore(DbContext context) : base(context) { } } [TestMethod] public async Task CustomUserGuidKeyTest() { var manager = new UserManager<GuidUser, Guid>(new GuidUserStore(new GuidUserContext())); GuidUser[] users = { new GuidUser() { UserName = "test" }, new GuidUser() { UserName = "test1" }, new GuidUser() { UserName = "test2" }, new GuidUser() { UserName = "test3" } }; foreach (var user in users) { UnitTestHelper.IsSuccess(await manager.CreateAsync(user)); } foreach (var user in users) { var u = await manager.FindByIdAsync(user.Id); Assert.IsNotNull(u); Assert.AreEqual(u.UserName, user.UserName); } } 
+30
Oct 24 '13 at 21:34
source share

Using Stefan Cebulak's answer and a great Ben Foster blog article ASP.NET Bare Authentication I came up with the solution below that I applied to ASP.NET Identity 2.0 generated by Visual Studio 2013 AccountController .

The solution uses an integer as the primary key for users, and also allows you to get the identifier of the current user without making a trip to the database.

Here are the steps you need to follow:

1. Creating user-defined custom classes

By default, AccountController uses classes that use string as the type of primary key. We need to create classes below that will be used instead of int . I defined all the classes below in one file: AppUser.cs

 public class AppUser : IdentityUser<int, AppUserLogin, AppUserRole, AppUserClaim>, IUser<int> { } public class AppUserLogin : IdentityUserLogin<int> { } public class AppUserRole : IdentityUserRole<int> { } public class AppUserClaim : IdentityUserClaim<int> { } public class AppRole : IdentityRole<int, AppUserRole> { } 

It will also be useful to create a custom ClaimsPrincipal that easily displays the user id

 public class AppClaimsPrincipal : ClaimsPrincipal { public AppClaimsPrincipal( ClaimsPrincipal principal ) : base( principal ) { } public int UserId { get { return int.Parse(this.FindFirst( ClaimTypes.Sid ).Value); } } } 

2. Create a custom IdentityDbContext

In our application database context, IdentityDbContext will expand by default, which implements all authentication related DbSets. Even if DbContext.OnModelCreating is an empty method, I'm not sure about IdentityDbContext.OnModelCreating , so when overriding, remember to call base.OnModelCreating( modelBuilder ) AppDbContext.cs

 public class AppDbContext : IdentityDbContext<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim> { public AppDbContext() : base("DefaultConnection") { // Here use initializer of your choice Database.SetInitializer( new CreateDatabaseIfNotExists<AppDbContext>() ); } // Here you define your own DbSet's protected override void OnModelCreating( DbModelBuilder modelBuilder ) { base.OnModelCreating( modelBuilder ); // Here you can put FluentAPI code or add configuration map's } } 

3. Create custom UserStore and UserManager to be used above

AppUserStore.cs

 public interface IAppUserStore : IUserStore<AppUser, int> { } public class AppUserStore : UserStore<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>, IAppUserStore { public AppUserStore() : base( new AppDbContext() ) { } public AppUserStore(AppDbContext context) : base(context) { } } 

AppUserManager.cs

 public class AppUserManager : UserManager<AppUser, int> { public AppUserManager( IAppUserStore store ) : base( store ) { } } 

4. Modify AccountController to use your custom classes

Change all UserManager to AppUserManager , UserStore to AppUserStore , etc. Take an example of these constructors:

 public AccountController() : this( new AppUserManager( new AppUserStore( new AppDbContext() ) ) ) { } public AccountController(AppUserManager userManager) { UserManager = userManager; } 

5. Add the user ID as a claim to the ClaimIdentity stored in the cookie

In step 1, we created an AppClaimsPrincipal that returns a UserId from ClaimType.Sid . However, in order to have this claim, we need to add it when registering with the user. In AccountController the SingInAsync method SingInAsync responsible for logging in. We need to add a line to this method in order to add a ticket.

 private async Task SignInAsync(AppUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); // Extend identity claims identity.AddClaim( new Claim( ClaimTypes.Sid, user.Id.ToString() ) ); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } 

6. Create a BaseController with the CurrentUser property

To have easy access to the already registered user ID in your controllers, create an abstract BaseController from which your controllers will be displayed. In BaseController create CurrentUser as follows:

 public abstract class BaseController : Controller { public AppClaimsPrincipal CurrentUser { get { return new AppClaimsPrincipal( ( ClaimsPrincipal )this.User ); } } public BaseController() { } } 

7. Inherit your BaseController controllers and enjoy

From now on, you can use CurrentUser.UserId in your controllers to access the current user ID in the system without disconnecting from the database. You can use it to query only objects belonging to the user.

You don’t have to worry about automatically generating the primary keys of the user - it is not surprising that the Entity Framework by default uses Identity for whole primary keys when creating tables.

Warning! Keep in mind that if you implement it in an already released project, for already registered users ClaimsType.Sid will not exist, and FindFirst will return null in AppClaimsPrincipal . You need to either force all users to log out or run this script in AppClaimsPrincipal

+52
May 09 '14 at 20:03
source share

@HaoKung

I managed to make an int id with your nightly builds. The problem of User.Identity.GetUserId () still exists, but I just did int.parse ().

The biggest surprise was that I did not need to create an ID myself, db was created with an identifier identifier, and it was somehow automatically set for new Oo users ...

Model:

  public class ApplicationUser : IdentityUser<int, IntUserLogin, IntUserRole, IntUserClaim> { public ApplicationUser() { } public ApplicationUser(string name) : this() { UserName = name; } } public class ApplicationDbContext : IntUserContext { public ApplicationDbContext() { } } private class IntRole : IdentityRole<int, IntUserRole> { public IntRole() { } public IntRole(string name) : this() { Name = name; } } private class IntUserRole : IdentityUserRole<int> { } private class IntUserClaim : IdentityUserClaim<int> { } private class IntUserLogin : IdentityUserLogin<int> { } private class IntUserContext : IdentityDbContext<ApplicationUser, IntRole, int, IntUserLogin, IntUserRole, IntUserClaim> { public IntUserContext() : base("DefaultConnection") { } } private class IntUserStore : UserStore<ApplicationUser, IntRole, int, IntUserLogin, IntUserRole, IntUserClaim> { public IntUserStore(DbContext context) : base(context) { } } private class IntRoleStore : RoleStore<IntRole, int, IntUserRole> { public IntRoleStore(DbContext context) : base(context) { } } 

Controller:

  public AccountController() : this(new UserManager<ApplicationUser, int>(new IntUserStore(new ApplicationDbContext()))) { } public AccountController(UserManager<ApplicationUser, int> userManager) { UserManager = userManager; } public UserManager<ApplicationUser, int> UserManager { get; private set; } 

Hope build release coming soon: D ...

PS I can not write comments, so I made an answer, sorry.

+6
Jan 02 '14 at 2:30
source share

As indicated here :

In Visual Studio 2013, the web application by default uses the string value for the key for user accounts. ASP.NET Identity allows you to change the key type to meet your data requirements. For example, you can change the key type from a string to an integer.

In this section, the link above shows how to get started with the default web application and change the user account key to an integer. You can use the same modifications to implement any type of key in your project. It shows how to make these changes to the default web application, but you can apply the same changes to the user application. It shows the changes needed when working with MVC or web forms.

+4
Feb 13 '15 at 5:58
source share

Basically you should:

-Change the key type for int in the Identity user class
-Add custom identity classes that use int as a key
-Change the context class and user manager to use int as a key
- Set launch configuration to use int as key
-Change AccountController to pass int as keys

here is a link where all the steps are explained to achieve this.

+3
Jul 28 '14 at 16:28
source share

If you are using ASP.NET Identity 3.0 (beta 6), there is a very direct solution, but I tried so hard to find the documentation on the Internet. After some trial and error, I found a good simple solution and posted it here: http://mycodelog.com/2015/08/18/asp-net-vnext-identity-3-0-beta6-in-mvc-6-beta6 -spa-using-integer-keys-instead-of-strings /

0
Aug 19 '15 at 5:28
source share



All Articles