Как сделать EF-Core использовать Guid вместо String для своего ID/Первичного ключа
Когда я смотрю на ASP.NET 3 Identity, он использует string
, а не Guid
для уникального первичного ключа.
В моем классе Entity Framework
code first
Пользователи ApplicationUser
я наследую класс Identity
public class ApplicationUser : IdentityUser
{
}
что приводит к тому, что при создании миграции Entity Framework создается таблица aspnetusers
, созданная с типом ключа nvarchar(450)
вместо uniqueidentifier
Когда я сравниваю это с проектом ASP.NET Identity 2
Entity Framework
, он создал поле идентификатора uniqueidentifier
, а не nvarchar(450)
Я бы предположил, что для производительности базы данных первичные ключи и внешние ключи uniqueidentifier
будут лучше, чем nvarchar(450)
Есть ли способ использовать уникальный ключ Guid
вместо string
и uniqueidentifier
вместо nvarchar(450)
с помощью ASP.NET Identity 3
?
Существует предыдущий вопрос, как преобразовать String в Guid, но я хочу, чтобы идентификатор таблицы базы данных был ориентиром.
Я нашел еще один question и для предыдущей BETA, когда длина была nvarchar (128). Причина в том, что не все базы данных поддерживают гиды, и это было изменено для гибкости.
Должен быть простой способ изменить строку на строку Guid без перезаписи целого identity 3
?
nvarchar(450)
действительно избыточно и дает всевозможные предупреждения при создании ограничений базы данных SQL Server. Администраторы базы данных будут окончательно не похожи на эти предупреждения.
Ответы
Ответ 1
Вам нужно настроить ApplicationUser
наследование с IdentityUser<TKey>
и наследование по умолчанию из IdentityRole<TKey>
public class ApplicationUser : IdentityUser<Guid> { }
public class Role : IdentityRole<Guid> { }
Пользовательский класс контекста наследует от IdentityDbContext<ApplicationUser, Role, TKey>
и использует свободно api для автоматического генерации ключей guid.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Role, Guid>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(b =>
{
b.Property(u => u.Id).HasDefaultValueSql("newsequentialid()");
});
builder.Entity<Role>(b =>
{
b.Property(u => u.Id).HasDefaultValueSql("newsequentialid()");
});
}
}
затем в Startup добавьте службу Identity в контейнер, подобный этому
services.AddIdentity<ApplicationUser, Role>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders()
.AddUserStore<UserStore<ApplicationUser, Role, ApplicationDbContext, Guid>> ()
.AddRoleStore<RoleStore<Role, ApplicationDbContext, Guid>>();
Если вы не создали базу данных, очистите папку миграции и запустите команды ef
Ответ 2
Я еще не испортил миграции для этого примера (им могут потребоваться некоторые настройки), однако ASP.NET Identity v3 гораздо более расширяема, чем v2 в этом отношении.
Следующее должно предоставить вам хранилище Identity на основе основных ключей Guid для пользователей и ролей:
public class ApplicationUser : IdentityUser<Guid>
{
}
public class GuidDataContext :
IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
{
}
и в вашем классе запуска:
services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(
identity =>
{
// whatever identity options you want
identity.User.RequireUniqueEmail = true;
identity.Password.RequiredLength = 8;
}).
AddEntityFrameworkStores<GuidDataContext, Guid>().AddDefaultTokenProviders();
Аналогичным образом, если вам не нужно добавлять какие-либо настраиваемые поля в Identity User или настраивать параметры, вы можете просто сделать следующее:
public class GuidDataContext :
IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>
{
}
и при запуске:
services
.AddIdentity<IdentityUser<Guid>, IdentityRole<Guid>>()
.AddEntityFrameworkStores<GuidDataContext, Guid>()
.AddDefaultTokenProviders();
Ответ 3
ApplicationUser наследует IdentityUser базовый класс, который определяется как имеющий строку как id. Чтобы действительно использовать указатель guid/uniqueidentifier, вам не нужно наследовать этот базовый класс.
Обратите внимание, что внутри он использует директивные строки для идентификаторов. По крайней мере, вы должны ограничить размер поля ключа, как показано здесь:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(b =>
{
// you could limit the field size to just long enough for a guid id as string
b.Property(p => p.Id)
.HasMaxLength(36);
// instead, you could define the id as Guid but more work required
//b.Property(p => p.Id)
// .ForSqlServerHasColumnType("uniqueidentifier")
// .ForSqlServerHasDefaultValueSql("newid()")
// .IsRequired();
});
}
}
Можно использовать Guids/uniqueidentifier для ключа, но для этого потребуется больше работы, помимо использования собственного базового класса (или вообще не использовать базовый класс) и использовать собственный DbContext для сопоставления модели. Заимствование и изменение кода EF отсюда должно вас уволить, вам нужно было бы наследовать от UserStore и переопределить виртуальные методы поиска пользователя по id, поскольку интерфейс IUserStore определяет Подпись метода FindById со строкой. Таким образом, переопределяя, вам нужно будет преобразовать строку в guid внутри методов, где вам нужно найти или найти по id.
Я делаю именно это в моем проекте облаков, у которого есть индивидуальная реализация Identity с несколькими арендаторами
EDIT: на самом деле, ближе к коду IdentityUser имеет общую версию, где вы можете определить тип ключа
public class IdentityUser<TKey> where TKey : IEquatable<TKey>
Итак, вы можете просто определить свой собственный базовый класс, например
IdentityUser<Guid>
но я все же думаю, что вам нужно будет переопределить методы UserStore, которые берут строку id и преобразовать строку в guid.