Как изменить тип идентификатора в Microsoft.AspNet.Identity.EntityFramework.IdentityUser

(ASP.NET MVC 5, EF6, VS2013)

Я пытаюсь понять, как изменить тип поля "Id" из строки в int в типе:

Microsoft.AspNet.Identity.EntityFramework.IdentityUser

чтобы новые учетные записи пользователей были связаны с идентификатором целого числа, а не с идентификатором GUID. Но похоже, что это будет сложнее, чем просто добавить новое свойство Id с типом int в моем производном классе пользователя. Взгляните на эту подпись метода:

(из сборки 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 является строкой. Нужно ли мне переопределять эти классы?

Объяснение того, почему я не хочу хранить идентификаторы GUID для идентификаторов в таблице пользователя:

. Будут другие таблицы, которые связывают данные с таблицей пользователей через внешний ключ. (Когда пользователи сохраняют контент на сайте.) Я не вижу причин использовать более широкий тип поля и тратить дополнительное пространство базы данных без явных преимуществ. (Я знаю, что есть другие сообщения об использовании GUID vs int ids, но кажется, что многие считают, что int ids быстрее и используют меньше места, что все еще оставляет мне интересно.)

- Я планирую выставить спокойную конечную точку, чтобы пользователи могли получать данные о конкретном пользователе. Я думаю:

/users/123/name

чище, чем

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

Кто-нибудь знает, почему команда ASP.NET решила реализовать идентификаторы таким образом? Я не подозреваю, что пытаюсь изменить это на тип int? (Возможно, есть преимущества, которые мне не хватает.)

Спасибо...

-Бен

Ответ 1

Итак, если вы хотите установить идентификаторы, вам нужно создать свой собственный класс POCO IUser и реализовать свой IUserStore для вашего пользовательского класса IUser в версии RTM версии 1.0.

Это то, что у нас не было времени для поддержки, но сейчас я изучаю это легко (ier) в 1.1. Надеюсь, что кое-что будет доступно в ночных сборках в ближайшее время.

Обновлен с примером 1.1-alpha1: Как получить ночные постройки

Если вы обновляетесь до последних ночных бит, вы можете попробовать новый 1.1-alpha1 apis, который должен сделать это проще сейчас: здесь то, что включает в себя Гиды вместо строк, должно выглядеть, например,

    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

Используя Stefan Cebulak ответ и замечательную статью в блоге Ben Foster Идентификация ASP.NET Bare Я придумал ниже решение, которое я применил к ASP.NET Identity 2.0, сгенерированный Visual Studio 2013 AccountController.

Решение использует целое число как первичный ключ для пользователей, а также позволяет получить идентификатор текущего пользователя, не совершая поездки в базу данных.

Вот шаги, которые вам нужно выполнить:

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, который легко выведет идентификатор пользователя

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, который по умолчанию реализует все DbSets, связанные с аутентификацией. Даже если 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. Создайте пользовательские UserStore и UserManager, которые будут использоваться выше

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. Добавьте идентификатор пользователя в качестве претензии в 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);
}

6. Создайте BaseController с свойством CurrentUser

Чтобы иметь легкий доступ к уже зарегистрированному идентификатору пользователя в ваших контроллерах, создайте абстрактный BaseController, из которого будут выводиться ваши контроллеры. В BaseController создайте CurrentUser следующим образом:

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


    public BaseController()
    {

    }
}

7. Наследуйте свои контроллеры от BaseController и наслаждайтесь

С этого момента вы можете использовать CurrentUser.UserId в своих контроллерах для доступа к идентификатору текущего пользователя в системе без отключения в базе данных. Вы можете использовать его для запроса только объектов, принадлежащих пользователю.

Вам не нужно заботиться об автоматической генерации первичных ключей пользователя - не удивительно, что Entity Framework по умолчанию использует Identity для целых первичных ключей при создании таблиц.

Предупреждение! Имейте в виду, что если вы реализуете его в уже выпущенном проекте, для уже зарегистрированных пользователей ClaimsType.Sid не будет существовать, а FindFirst вернет значение null в AppClaimsPrincipal. Вам нужно либо принудительно вывести всех пользователей, либо выполнить этот сценарий в AppClaimsPrincipal

Ответ 3

@HaoKung

Мне удалось сделать int id с вашими ночными сборками. Проблема User.Identity.GetUserId() все еще существует, но я только что сделал int.parse().

Самый большой сюрприз заключался в том, что мне не нужно было создавать ID самостоятельно, db был создан с идентификатором идентификатора, и он был как-то автоматически установлен для новых пользователей 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; }

Скоро появится релиз сборки Hope: D...

P.S. Не могу написать комментарии, поэтому я сделал ответ, извините.

Ответ 4

Как указано здесь:

В Visual Studio 2013 веб-приложение по умолчанию использует строковое значение для ключа для учетных записей пользователей. ASP.NET Identity позволяет вам изменить тип ключа для удовлетворения ваших требований к данным. Например, вы можно изменить тип ключа из строки в целое число.

В этом разделе по приведенной выше ссылке показано, как начать работу с веб-приложением по умолчанию и изменить ключ учетной записи пользователя на целое число. Вы можете использовать те же самые модификации для реализации любого типа ключа в вашем проекте. В нем показано, как внести эти изменения в веб-приложение по умолчанию, но вы можете применить аналогичные изменения к пользовательскому приложению. Он показывает изменения, необходимые при работе с MVC или веб-формами.

Ответ 5

В основном вы должны:

-Изменить тип ключа для int в классе пользователя Identity
-Добавить настраиваемые классы удостоверений, которые используют int как ключ
-Измените класс контекста и пользовательский менеджер, чтобы использовать int как ключ
-Установить конфигурацию запуска, чтобы использовать int как ключ
-Измените AccountController для передачи int как клавиши

здесь является ссылкой, где все шаги объясняются для достижения этого.

Ответ 6

Если вы используете ASP.NET Identity 3.0 (бета-версия 6), есть очень прямое решение, но я так сильно изо всех сил пытался найти документацию в Интернете. После некоторых проб и ошибок я нашел хорошее простое решение и разместил его здесь: http://mycodelog.com/2015/08/18/asp-net-vnext-identity-3-0-beta6-in-mvc-6-beta6-spa-using-integer-keys-instead-of-strings/