可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
When I look at the ASP.NET 3 Identity it uses a string
and not a Guid
for the unique primary key.
In my Entity Framework
code first
Users' ApplicationUser
class I inherit the Identity class
public class ApplicationUser : IdentityUser
{
}
which results in when I create my Entity Framework migrations a table aspnetusers
getting created with a key type ofnvarchar(450)
instead of uniqueidentifier
When I compare this to ASP.NET Identity 2
ENtity Framework
project it created an ID field of uniqueidentifier
and not nvarchar(450)
I would imagine for database performance primary keys and foreign keys of uniqueidentifier
would be better than nvarchar(450)
Is there a way to use for the unique key Guid
instead of string
and uniqueidentifier
instead of nvarchar(450)
with ASP.NET Identity 3
?
There is a previous question how to convert a String to a Guid but I want the database table Id to be a Guid.
I found another question as well for a previous BETA when the length was nvarchar(128). The reason given is that not all databases support Guids and it was changed for flexibility.
Is there must be an easy way to change from string to Guid without rewriting the whole identity 3
?
nvarchar(450)
is really overkill and gives all kinds of warnings when creating SQL Server database constraints. Database admins will definitively not like these warnings.
回答1:
You need custom ApplicationUser
inherit from IdentityUser<TKey>
and custom Role inherit from IdentityRole<TKey>
public class ApplicationUser : IdentityUser<Guid> { }
public class Role : IdentityRole<Guid> { }
Custom context class inherit from IdentityDbContext<ApplicationUser, Role, TKey>
and use fluent api for auto generate guid keys.
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()");
});
}
}
then in Startup add Identity service to container like this
services.AddIdentity<ApplicationUser, Role>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders()
.AddUserStore<UserStore<ApplicationUser, Role, ApplicationDbContext, Guid>> ()
.AddRoleStore<RoleStore<Role, ApplicationDbContext, Guid>>();
If you have not created the database, clear the migrations folder and run ef commands
回答2:
I haven't messed with the migrations for this example yet (they might need some tweaks), however ASP.NET Identity v3 is far more extensible than v2 was in this regard.
The following should give you an Identity store based on Guid primary keys for Users and Roles alike:
public class ApplicationUser : IdentityUser<Guid>
{
}
public class GuidDataContext :
IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
{
}
and in your startup class:
services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(
identity =>
{
// whatever identity options you want
identity.User.RequireUniqueEmail = true;
identity.Password.RequiredLength = 8;
}).
AddEntityFrameworkStores<GuidDataContext, Guid>().AddDefaultTokenProviders();
Likewise, if you didn't need to add any custom fields to the Identity User, or customize your options, you could just do the following:
public class GuidDataContext :
IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>
{
}
and in your startup:
services
.AddIdentity<IdentityUser<Guid>, IdentityRole<Guid>>()
.AddEntityFrameworkStores<GuidDataContext, Guid>()
.AddDefaultTokenProviders();
回答3:
ApplicationUser inherits from IdentityUser base class which is defined as having a string as id. So to really use a guid/uniqueidentifier you would need to not inherit from that base class.
Note that internally it is using guid strings for ids. You should at least be able to limit the field size of the key like shown here:
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();
});
}
}
It is possible to use Guids/uniqueidentifier for the key but that will require more work, in addition to using your own base class (or not use a base class at all) and use a custom DbContext to map the model. Borrowing and modifying the EF code from here should get you going, you would have to inherit from UserStore and override the virtual methods for looking up a user by id, because the interface IUserStore defines the FindById method signature with a string. So overriding you would need to convert the string to a guid inside the methods where you need to fetch or find by id.
I'm doing exactly that in my cloudscribe project which has a custom multi-tenant implementation of Identity
EDIT: actually looking closer at the code the IdentityUser has a generic version where you can define the type of the key
public class IdentityUser<TKey> where TKey : IEquatable<TKey>
So you might be able to just define your own base class like
IdentityUser<Guid>
but I still think you would need to override the UserStore methods that take an id string and convert the string to a guid.