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

时间:2013-10-23 22:23:31

标签: asp.net-mvc entity-framework asp.net-mvc-5 owin asp.net-identity

(ASP.NET MVC 5,EF6,VS2013)

我正在试图找出如何在类型中将“Id”字段的类型从字符串更改为int

Microsoft.AspNet.Identity.EntityFramework.IdentityUser

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

(来自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);
  ...
  }

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

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

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

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

/users/123/name

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

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

...谢谢

-Ben

5 个答案:

答案 0 :(得分:52)

使用Stefan Cebulak的答案和Ben Foster的精彩博客文章ASP.NET Identity Stripped Bare我提出了以下解决方案,我已将其应用于ASP.NET身份 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> { }

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

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

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

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
    }
}

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

    }
}

AppUserManager.cs

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

    }
}

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

将所有UserManager更改为AppUserManager,将UserStore更改为AppUserStore等。举例说明此构造函数:

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

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

5。将用户ID添加为存储在Cookie中的ClaimIdentity

在第1步中,我们创建了AppClaimsPrincipal,其中公开了从ClaimType.Sid中取出的UserId。但是,要使此声明可用,我们需要在登录用户时添加它。在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);
}

6。创建一个BaseController属性

CurrentUser

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

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


    public BaseController()
    {

    }
}

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

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

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

警告!请注意,如果您在已发布的项目中实施,则对于已登录的用户ClaimsType.Sid将不存在,FindFirst将在AppClaimsPrincipal中返回null {1}}。您需要强制注销所有用户或在AppClaimsPrincipal

中处理此方案

答案 1 :(得分:30)

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

这是我们没有时间支持的事情,但我正在研究如何在1.1版本中轻松实现这个目标。希望很快就能在夜间建造中获得一些东西。

更新了1.1-alpha1示例: How to get nightly builts

如果您更新到最新的夜间位,您可以试用新的1.1-alpha1 api,这应该使现在更容易:这是插入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);
        }
    }

答案 2 :(得分:6)

@HaoKung

我已成功用你的夜间版本制作int id。 User.Identity.GetUserId()问题仍然存在,但我现在只做了int.parse()。

最大的惊喜是我不需要自己创建ID,db是使用身份ID制作的,并且它会以某种方式自动为新用户设置Oo ...

型号:

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

控制器:

        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; }

希望发布版本即将推出:D ...

P.S。不能写评论,所以我做了一个答案,对不起。

答案 3 :(得分:4)

在Visual Studio 2013中,默认Web应用程序使用字符串值作为用户帐户的密钥。 ASP.NET Identity允许您更改密钥的类型以满足您的数据要求。例如,您可以将键的类型从字符串更改为整数。

http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity

上述链接中的此主题显示了如何从默认Web应用程序开始并将用户帐户密钥更改为整数。您可以使用相同的修改来在项目中实现任何类型的键。它显示了如何在默认Web应用程序中进行这些更改,但您可以对自定义应用程序应用类似的修改。它显示了使用MVC或Web窗体时所需的更改。

答案 4 :(得分:3)

基本上你必须:

- 在Identity用户类中将键的类型更改为int - 添加使用int作为键的自定义Identity类 - 更改上下文类和用户管理器以使用int作为键
- 更改启动配置以使用int作为键
- 更改AccountController以将int作为键传递

here是解释所有步骤以实现此目的的链接。