How to change type of id in Microsoft.AspNet.Identity.EntityFramework.IdentityUser

asp.net-identity asp.net-mvc asp.net-mvc-5 entity-framework owin

Question

(VS2013, ASP.NET MVC 5, EF6)

I'm attempting to determine how to type the "Id" field's type should be changed from string to int. in the format:

Microsoft.AspNet.Identity.EntityFramework.IdentityUser

in order to replace the GUID associated with new user accounts with an integer ID. However, it appears that this will be more difficult than merely introducing a new Id attribute of type int in my derived user class. Look at the method signature below:

(Extracted from the Assembly Microsoft.AspNet.Identity.Core.dll)

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

It therefore appears that there are other built-in ASP.NET identity framework functions that call for the userId to be a string. Do I also have to reimplement these classes?

Why I don't want to keep GUIDs as user table ids is as follows:

-Other tables will have a foreign key that links their data to the users table. (When site visitors store stuff.) I don't see any justification for using the larger field type and using up additional database space for nothing. (I am aware that there have been numerous posts comparing the use of int ids vs. GUIDs, but it appears like many have claimed that int ids are quicker and take up less space, which still begs the question.)

To enable users to access information about a specific person, I intend to publish a restful endpoint. I believe:

/users/123/name

is more pristine

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

Does anyone know the reasoning behind the ASP.NET team's choice to implement IDs in this manner? Am I being naive in my attempt to convert this to an int type? (Perhaps there are advantages I'm not considering.)

Thanks...

-Ben

1
67
10/23/2013 10:23:31 PM

Accepted Answer

ZZZ_tmp
30
11/12/2013 6:01:09 PM

Popular Answer

Using Sebastian Cebulak's response and Identity Stripped From ASP.NET's excellent blog post, With a produced by Visual Studio 2013 solution, I applied it to ASP.NET Identity version 2.0.AccountController .

The approach employs an integer as a primary key for users and additionally enables retrieval of the ID of the user who is presently logged in without accessing the database.

The steps you must take are as follows:

1. Design unique classes for users

Standardly, theAccountController utilizes classes, which arestring is an example of a primary key type. The following courses, which will make use of anint instead. I defined the following classes in a single 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> { }

Having a customized ClaimsPrincipal that can quickly reveal the User's ID will also be helpful.

public class AppClaimsPrincipal : ClaimsPrincipal
{
    public AppClaimsPrincipal( ClaimsPrincipal principal ) : base( principal )
    { }

    public int UserId
    {
        get { return int.Parse(this.FindFirst( ClaimTypes.Sid ).Value); }
    }
}

2. Construct a uniqueIdentityDbContext

The database context of our application will be expandedIdentityDbContext which by default supports all DbSets relevant to authentication. No matterDbContext.OnModelCreating is a useless method, and I'm not sure how theIdentityDbContext.OnModelCreating , therefore be sure to call when overridingbase.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. Construct uniqueUserStore and UserManager using the aforementioned

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. ChangeAccountController to utilize your personal classes

All changesUserManager to AppUserManager , UserStore to AppUserStore etc. Take this constructor as an example:

public AccountController()
    : this( new AppUserManager( new AppUserStore( new AppDbContext() ) ) )
{
}

public AccountController(AppUserManager userManager)
{
    UserManager = userManager;
}

5. Add the user's ID to the claim.ClaimIdentity kept in a cookie.

Step 1 involved creatingAppClaimsPrincipal , which discloses UserId extracted fromClaimType.Sid . However, we must add it when the user logs in for this claim to be available. InAccountController a SingInAsync The method is in charge of logging in. To this approach, we must add a line with the claim.

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. Make aBaseController with aCurrentUser property

Create an abstract variable to give your controllers quick access to a user's ID that is currently logged in.BaseController which your controllers will take their cue. In theBaseController , develop aCurrentUser the following

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


    public BaseController()
    {

    }
}

7. Take over the controllers fromBaseController and delight

As of right now, you can useCurrentUser.UserId to access a user's ID who is currently logged in without going to the database in your controllers. You can use it to exclusively query the user's own objects.

As expected, Entity Framework uses Identity for integer primary keys when constructing tables, so you don't have to worry about the automatic generation of user primary keys.

Warning! Just keep in mind that if you implement it for already logged-in users in a project that has already been released.ClaimsType.Sid won't be there, andFindFirst will result in nullAppClaimsPrincipal You must either compel all users to log out or deal with this situation inAppClaimsPrincipal



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow