如何在Microsoft.AspNet.Identity.EntityFramework.IdentityUser中更改id的类型

Ben*_*iFB 66 asp.net-mvc entity-framework owin asp.net-mvc-5 asp.net-identity

(ASP.NET MVC 5,EF6,VS2013)

我试图弄清楚如何在类型中将"Id"字段的类型从字符串更改为int:

Microsoft.AspNet.Identity.EntityFramework.IdentityUser
Run Code Online (Sandbox Code Playgroud)

为了使新用户帐户与整数ID而不是GUID相关联.但似乎这比在我的派生用户类中简单地添加类型为int的新Id属性更复杂.看看这个方法签名:

(来自程序集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);
  ...
  }
Run Code Online (Sandbox Code Playgroud)

因此,似乎ASP.NET身份框架中还有其他方法要求userId为字符串.我还需要重新实现这些课程吗?

解释为什么我不想在用户表中存储id的GUID:

- 将有其他表通过外键将数据与users表相关联.(当用户在网站上保存内容时.)我认为没有理由使用较大的字段类型并花费额外的数据库空间而没有明显的优势.(我知道还有其他关于使用GUID和int ID的帖子,但似乎有很多人建议int id更快并且使用更少的空间,这仍然让我感到疑惑.)

- 我计划公开一个restful端点,以允许用户检索有关特定用户的数据.我认为:

/users/123/name
Run Code Online (Sandbox Code Playgroud)

比...更干净

/users/{af54c891-69ba-4ddf-8cb6-00d368e58d77}/name
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么ASP.NET团队决定以这种方式实现ID?在尝试将其更改为int类型时,我是否只是短视?(也许我缺少一些好处.)

谢谢...

-ben

krz*_*chu 52

使用Stefan Cebulak的回答和Ben Foster的伟大博客文章ASP.NET Identity Stripped Bare我提出了以下解决方案,我已经将其应用于ASP.NET Identity 2.0并使用Visual Studio 2013生成AccountController.

该解决方案使用整数作为用户的主键,并且还允许获取当前登录用户的ID而无需访问数据库.

以下是您需要遵循的步骤:

1.创建与用户相关的自定义类

默认情况下,AccountController正在使用的使用类string作为主键的类型.我们需要创建以下类,int而不是使用.我在一个文件中定义了以下所有类: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> { }
Run Code Online (Sandbox Code Playgroud)

拥有自定义的ClaimsPrincipal也很有用,它可以轻松公开用户的ID

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

    public int UserId
    {
        get { return int.Parse(this.FindFirst( ClaimTypes.Sid ).Value); }
    }
}
Run Code Online (Sandbox Code Playgroud)

2.创建自定义 IdentityDbContext

我们的应用程序的数据库上下文将扩展IdentityDbContext,默认情况下实现所有与身份验证相关的DbSet.即使DbContext.OnModelCreating是一个空方法,我也不确定IdentityDbContext.OnModelCreating,所以当重写时,记得打电话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
    }
}
Run Code Online (Sandbox Code Playgroud)

3.创建自定义UserStoreUserManager,将上述使用

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)
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

AppUserManager.cs

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

    }
}
Run Code Online (Sandbox Code Playgroud)

4.修改AccountController以使用您的自定义类

将所有UserManagerAppUserManager,UserStoreAppUserStore等拿这个构造的一个例子:

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

public AccountController(AppUserManager userManager)
{
    UserManager = userManager;
}
Run Code Online (Sandbox Code Playgroud)

5.添加用户ID作为ClaimIdentity存储在cookie中的声明

在步骤1中,我们已创建AppClaimsPrincipal,其中公开了UserId ClaimType.Sid.但是,要使此声明可用,我们需要在登录用户时添加它.在AccountController一个SingInAsync方法中负责登录.我们需要在此方法中添加一行,以添加声明.

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);
}
Run Code Online (Sandbox Code Playgroud)

6. BaseController使用CurrentUser属性创建一个

要轻松访问控制器中当前登录用户的ID,请创建一个抽象BaseController,控制器将从中派生.在BaseController,创建CurrentUser如下:

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


    public BaseController()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

7.继承您的控制器BaseController并享受

从现在开始,您可以CurrentUser.UserId在控制器中使用访问当前登录用户的ID而无需访问数据库.您可以使用它来仅查询属于该用户的对象.

您不必自动生成用户主键 - 毫不奇怪,在创建表时,Entity Framework默认使用Identity作为整数主键.

警告!请记住,如果您在已发布的项目中实现它,对于已登录的用户ClaimsType.Sid将不存在FindFirst并将返回null AppClaimsPrincipal.您需要强制注销所有用户或处理此方案AppClaimsPrincipal


Hao*_*ung 30

因此,如果您需要int ID,则需要创建自己的POCO IUser类,并在1.0 RTM版本中为您的自定义IUser类实现IUserStore.

这是我们没有时间支持的东西,但我现在正在考虑在1.1中轻松实现这个目标.希望很快就可以在夜间制作中获得一些东西.

更新了1.1-alpha1示例: 如何获得夜间建筑

如果您更新到最新的夜间位,您可以尝试新的1.1-alpha1 apis,这应该使这更容易:这是插入Guids而不是字符串应该是什么样的例子

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • @Hao - 为什么在数据库中使用"string"字段而不是uniqueidentifier实现?有什么办法可以在数据库中使用sql uniqueidentifier吗? (3认同)

Ste*_*lak 6

@HaoKung

I've succeeded to make int id's with your nightly builds. User.Identity.GetUserId() problem is still there, but i just did int.parse() for now.

The biggest suprise was that i did not need to create ID by myself, db was made with identity id and it was somehow automatically set for new users Oo...

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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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; }
Run Code Online (Sandbox Code Playgroud)

Hope release build will come soon :D...

P.S. Can't write comments so i did an answer, sorry.