Как да промените типа на id в 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 за идентификатори в потребителската таблица:

-Ще има други таблици, които свързват данни с таблицата на потребителите чрез външен ключ. (Когато потребителите запазват съдържание на сайта.) Не виждам причина да използвам по-големия тип поле и да изразходвам допълнително място в базата данни без ясни предимства. (Знам, че има други публикации за използването на GUIDs срещу int ids, но изглежда, че мнозина предполагат, че int ids са по-бързи и използват по-малко място, което все още ме кара да се чудя.)

- Планирам да изложа спокойна крайна точка, за да позволя на потребителите да извличат данни за конкретен потребител. Аз мисля:

/users/123/name

е по-чист от

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

Някой знае ли защо екипът на ASP.NET реши да внедри идентификатори по този начин? Дали съм късоглед, опитвайки се да променя това на int тип? (Може би има предимства, които пропускам.)

Благодаря...

-Бен


person BenjiFB    schedule 23.10.2013    source източник
comment
Актуализирах отговора си с примерен код за това как да променя типа в най-новия нощен 1.1-alpha1   -  person Hao Kung    schedule 12.11.2013
comment
Забележка към бъдещите читатели на този въпрос: ASP.NET Identity Версия 2.0.0 (издадена на 20 март 2014 г.) включва вградена възможност за промяна/разширяване на типа на ID/първичния ключ. Вижте blogs.msdn.com/b/webdev/archive/2014/03/20/   -  person Funka    schedule 19.05.2014
comment
а за бъдещите читатели има примерно решение с User.ID като цяло число: aspnet.codeplex.com/SourceControl/latest#Samples/Identity/   -  person trailmax    schedule 04.08.2014
comment
Има добре обяснена официална статия за ASP.NET по темата: Промяна на първичния ключ за потребители в самоличността на ASP.NET   -  person Deilan    schedule 21.01.2015
comment
Вашите притеснения за потребителския интерфейс не трябва да представляват интерес за екипа по внедряване на сигурност!   -  person Pascal    schedule 13.03.2017


Отговори (5)


Работи като чар. Оценявам го. Също така, знаете ли защо sendTextMessage и sendMultipartTextMessage съществуват едновременно?
person Hao Kung    schedule 24.10.2013
comment
Благодаря за пояснението, Хао. Можете ли да обясните защо са използвани GUID вместо int? - person BenjiFB; 25.10.2013
comment
Така че решихме да използваме ключове за низове, за да избегнем проблеми с сериализацията на ключове, внедряването по подразбиране на EF можеше да използва ints като първичен ключ, GUID бяха просто лесен начин за генериране на произволен уникален ключ за низ. - person Hao Kung; 25.10.2013
comment
@Hao - защо реализацията използва поле за низ в базата данни вместо уникален идентификатор? има ли начини да използвам sql uniqueidentifier в базата данни? - person Piotr Stulinski; 26.10.2013
comment
@Hao, благодаря за примера, но можеш ли да дадеш пример, при който използваш int32 като тип за първоначалния въпрос? Изглежда, че това би било малко по-сложно, защото с примера с GUID вие просто искате нов GUID, но предполагам, че процедурата би била различна за int. Благодаря... - person BenjiFB; 12.11.2013
comment
Също така, къде можем да намерим нощните части? - person BenjiFB; 12.11.2013
comment
добави връзката за nightly към отговора, за int можете просто да замените guid с int и EF автоматично ще генерира ключ за вас, така че можете да премахнете NewGuid. - person Hao Kung; 12.11.2013
comment
Супер! Ще пробвам. Благодаря @HaoKung - person BenjiFB; 13.11.2013
comment
@HaoKung, това изглежда се проваля за мен, когато се опитвам да използвам int вместо GUID. Една такава грешка: Този код идва от алфа версията на Assembly Microsoft.AspNet.Identity.Core.dll: if (model.LoginProvider == LocalLoginProvider) { result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId()); } Той все още връща низ за ID. Но в примера за SPA вижте това: резултат = изчакайте UserManager.RemovePasswordAsync(User.Identity.GetUserId()); RemovePasswordAsync вече очаква цяло число, но получава низ. - person BenjiFB; 14.11.2013
comment
Можете ли да ме уведомите кога ще има налична документация за това? Това е много трудно без документация. Благодаря... - person BenjiFB; 14.11.2013
comment
Така че има два различни проблема, потребителският идентификатор в ClaimsIdentity все още ще бъде представен като низ в искането, ще трябва сами да го превърнете в guid от низ в шаблона, преди да го предадете в потребителския мениджър. т.е. нов Guid(User.Identity.GetUserId()) - person Hao Kung; 14.11.2013
comment
Или можете да напишете свой собствен метод за разширение User.Identity.GetUserGuid, който скрива това... - person Hao Kung; 14.11.2013
comment
@BenjiFB, имахте ли късмет с това? Имам потребителски модел, който има свойство за целочислен идентификатор и наистина не искам да го променям. Разбира се, за да бъда честен, аз също не искам да използвам алфа код, така че ще се интересувам от решение, базирано на RTM, ако това изобщо е възможно. Не можах да разбера нищо с персонализиран IUserStore, тъй като интерфейсът на IUser изисква идентификатор на низ, което противоречи на съществуващото ми свойство. - person dwhite; 20.11.2013
comment
@dwhite, не, не успях да направя това. Дори не опитах без алфа (не съм сигурен дали е възможно с RTM битовете), но дори и с алфа открих, че трябва да направя каскадно много промени, така че реших, че предпочитам поне да изчакам вече не беше алфа, преди да инвестирам в промените. - person BenjiFB; 20.11.2013
comment
@HaoKung - с Identity 2.0 beta1, ако искаме да използваме Guid като pkey, все още ли трябва да дефинираме GuidUserStore и GuidRoleStore? т.е. трябва ли да променим горния ви код по някакъв начин? - person James Reategui; 20.02.2014
comment
Трябва да е горе-долу същото за бета, както по-горе. - person Hao Kung; 20.02.2014
comment
Трябва ли да регистрирам нови модели на идентичност с DI (използвам Unity), защото получавам грешка The type IUserStore'2 does not have an accessible constructor. и изглежда, че това е проблем с тези модели? - person demo; 15.06.2015
comment
@HaoKung Гледам и публикацията на asp.net/identity/overview/extensibility/, но не съм сигурен дали трябва да добавя персонализирани класове към IdentityModels, както е споменато в стъпка Добавяне на персонализирани класове за идентичност, които използват типа ключ на тази страница. Тъй като сега създавам проекта и в момента няма създадена таблица. Искам да кажа, че мога ли да променя текущите класове, вместо да добавя персонализирани? - person Jack; 31.07.2016

Използвайки отговора на Stefan Cebulak и страхотната статия в блога на Ben Foster ASP.NET Identity Stripped Bare Измислих решение по-долу, което приложих към ASP .NET идентичност 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, който лесно ще разкрие 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, който прилага по подразбиране всички 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. Добавете ID на потребителя като иск към ClaimIdentity, съхранен в бисквитка

В стъпка 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 по подразбиране използва идентичност за целочислени първични ключове, когато създава таблици.

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

person krzychu    schedule 09.05.2014
comment
всичко работи, освен това FindFirst(ClaimTypes.Sid) винаги ми дава нула, така че получих изключение в AppClaimsPrincipal.UserId. Изглежда, че след влизане потребителят не е получил нов иск (добавих вашия код) - person Piotr M; 29.01.2016
comment
Сигурни ли сте, че целият код в стъпка 5 се изпълнява? Също така, моля, изтрийте всяка пътека, която използва вашето влизане (напр. бисквитка) и вижте дали е зададено правилно след влизане. - person krzychu; 29.01.2016
comment
Да Сигурен съм. Потребителят се регистрира със самоличност с всички необходими претенции. Когато търся потребител в контролера, той има само 1 претенция (на GetAuthenticationManager().SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); ред има 4 претенции. - person Piotr M; 29.01.2016
comment
Добре, изглежда, че не можете да получите потребителско име в контролера, когато потребителят влиза в системата. След пренасочване всичко работи добре! - person Piotr M; 01.02.2016
comment
@krzychu Гледам също публикацията на asp.net/identity/overview/extensibility/, но не съм сигурен дали трябва да добавя персонализирани класове към IdentityModels, както е споменато в стъпка Добавяне на персонализирани класове за идентичност, които използват типа ключ на тази страница. Тъй като сега създавам проекта и в момента няма създадена таблица. Искам да кажа, че мога ли да променя текущите класове, вместо да добавя персонализирани? - person Jack; 31.07.2016

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

Надяваме се, че версията ще дойде скоро :D...

P.S. Не мога да пиша коментари, затова отговорих, съжалявам.

person Stefan Cebulak    schedule 02.01.2014
comment
Стефане, какво направи за ChallengeResult() и AuthenticationManager.GetExternalLoginInfoAsync(). И двата метода се намират в AccountController() и и двата очакват UserId като параметър, но като string. Така че не можете да го конвертирате в int. Анализирахте ли и User.Identity.GetUserId() на int или не? - person Quoter; 22.03.2014
comment
Не, честно казано дори не забелязах това, но удостоверяването от Google и Facebook работи правилно. Мисля, че няма да го променям, а по-скоро да изчакам версията на версията. Това изглежда има нещо общо с XSRF защитата. - person Stefan Cebulak; 22.03.2014
comment
Какво имаш предвид с изчакване на версията? Най-новата версия на asp.net-identity беше пусната вчера? - person Quoter; 22.03.2014
comment
Това е приятна изненада :], вече е направена актуализация. Идентификаторът на низ все още е там по подразбиране в User.Identity.GetUserId(), чудя се дали трябва да създам свои собствени IdentityExtensions, за да променя това. Вероятно ще изчакам примери, нямам време да работя върху това в момента. - person Stefan Cebulak; 22.03.2014
comment
някой наистина ли е накарал това да работи? Току-що опитах и ​​получавам грешка „Поредица не съдържа съответстващ елемент“ при актуализиране на моята база данни. - person William; 03.05.2014
comment
@William Вече използвам това в един от моите проекти и досега работи добре, но създавах нова база данни с това, вместо да актуализирам съществуваща. - person Stefan Cebulak; 05.05.2014
comment
@Уилям, виж отговора ми. Ако срещнете проблеми, уведомете ме в коментарите. - person krzychu; 10.05.2014

Както е посочено тук:

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

Тази тема на връзката по-горе показва как да започнете с уеб приложението по подразбиране и да промените ключа на потребителския акаунт на цяло число. Можете да използвате същите модификации, за да приложите всеки тип ключ във вашия проект. Показва как да направите тези промени в уеб приложението по подразбиране, но можете да приложите подобни модификации към персонализирано приложение. Той показва промените, необходими при работа с MVC или уеб формуляри.

person Manik Arora    schedule 13.02.2015
comment
Този текст трябва да бъде цитиран правилно. - person Necreaux; 02.06.2015
comment
@Necreaux какво имаш предвид правилно цитиран? моля, бъдете ясни. - person t.durden; 21.08.2019
comment
@t.durden този първи абзац беше копиране/поставяне от свързания уебсайт без приписване и технически плагиатство. Тъй като никой не е направил нищо след този коментар, продължих и се опитах да го поправя. - person Necreaux; 21.08.2019

По принцип трябва да:

-Променете типа на ключа на int в потребителския клас за идентичност
-Добавете персонализирани класове за идентичност, които използват int като ключ
-Променете контекстния клас и потребителския мениджър, за да използвате int като ключ
- Променете конфигурацията за стартиране, за да използвате int като ключ
- Променете AccountController, за да прехвърлите int като ключ

тук е връзката, където са обяснени всички стъпки за постигане на това.

person lyz    schedule 28.07.2014