如何使 EF-Core 使用 Guid 而不是 String 作为其 ID/主键
Posted
技术标签:
【中文标题】如何使 EF-Core 使用 Guid 而不是 String 作为其 ID/主键【英文标题】:How to make EF-Core use a Guid instead of String for its ID/Primary key 【发布时间】:2016-05-11 14:46:01 【问题描述】:当我查看 ASP.NET 3 Identity 时,它使用 string
而不是 Guid
作为唯一主键。
在我的 Entity Framework
code first
用户的 ApplicationUser
类中,我继承了 Identity 类
public class ApplicationUser : IdentityUser
这导致当我创建我的实体框架迁移时,一个表 aspnetusers
使用nvarchar(450)
而不是uniqueidentifier
的键类型创建
当我将此与 ASP.NET Identity 2
ENtity Framework
项目进行比较时,它创建了一个 ID 字段 uniqueidentifier
而不是 nvarchar(450)
我想uniqueidentifier
的数据库性能主键和外键会比nvarchar(450)
更好
有没有办法使用唯一键 Guid
而不是 string
和 uniqueidentifier
而不是 nvarchar(450)
和 ASP.NET Identity 3
?
有一个previous question 如何将字符串转换为 Guid,但我希望数据库表 Id 是 Guid。
当长度为 nvarchar(128) 时,我还找到了另一个 question 以及以前的 BETA。给出的原因是并非所有数据库都支持 Guid,并且为了灵活性而对其进行了更改。
是否必须有一种简单的方法来从字符串更改为 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>
并使用 fluent 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()");
);
然后在启动时像这样将身份服务添加到容器中
services.AddIdentity<ApplicationUser, Role>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders()
.AddUserStore<UserStore<ApplicationUser, Role, ApplicationDbContext, Guid>> ()
.AddRoleStore<RoleStore<Role, ApplicationDbContext, Guid>>();
如果你还没有创建数据库,清除迁移文件夹并运行 ef 命令
【讨论】:
请注意,在 rc1-final 之前的 EntityFramework 7 / EntiryFramework Core 1.0 中存在一个错误,当在外键中使用泛型时会中断迁移,这些外键不会关闭内置类型 (像Guid)。这在 rc2-nightlies 中已经修复了很长一段时间,但还没有发布。 github.com/aspnet/EntityFramework/issues/3545 我对此表示赞同。它也适用于 .net core 2.2。它甚至比 msdn 上提供的代码更好。 这不适用于 ASP.Net Core 3.1 - AddEntityFrameworkStores 没有讲述 2 个通用参数的方法,第二个是类型 - 我们如何使用 ASP.Net Core 3.1 做到这一点? 如果我想将 IdentityUser 的键更改为 Guid 但将 Role 键保持为 int 怎么办?我尝试制作 Role : IdentityRole我还没有搞砸这个例子的迁移(他们可能需要一些调整),但是 ASP.NET Identity v3 在这方面比 v2 更可扩展。
以下内容应为您提供基于用户和角色的 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();
同样,如果您不需要向身份用户添加任何自定义字段或自定义选项,您可以执行以下操作:
public class GuidDataContext :
IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>
在您的创业公司中:
services
.AddIdentity<IdentityUser<Guid>, IdentityRole<Guid>>()
.AddEntityFrameworkStores<GuidDataContext, Guid>()
.AddDefaultTokenProviders();
【讨论】:
AddEntityFrameworkStores<GuidDataContext, Guid>()
来自哪里?我只有AddEntityFrameworkStores<GuidDataContext>()
,没有将第二种类型传递给AddEntityFrameworkStores
的选项
@O.MeeKoh 此响应是 4 年半前的响应,根据此后 API 的更改,它可能不再相关。我很乐意为您指出正确的方向,但不幸的是,在过去的几年里,我已经脱离了这个圈子。我会尽快看看当前的 API。【参考方案3】:
ApplicationUser 继承自 IdentityUser 基类,该基类被定义为具有字符串作为 id。因此,要真正使用 guid/uniqueidentifier,您不需要从该基类继承。
请注意,它在内部使用 guid 字符串作为 id。您至少应该能够限制键的字段大小,如下所示:
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 code from here 应该可以让您继续前进,您必须从UserStore 继承并覆盖用于通过id 查找用户的虚拟方法,因为接口IUserStore 使用字符串定义了FindById 方法签名。因此,您需要在需要通过 id 获取或查找的方法中将字符串转换为 guid。
我的 cloudscribe project 正是这样做的,它有一个自定义的多租户身份实现
编辑:实际上仔细查看 IdentityUser 的代码有一个通用版本,您可以在其中定义键的类型
public class IdentityUser<TKey> where TKey : IEquatable<TKey>
所以你也许可以像这样定义自己的基类
IdentityUser<Guid>
但我仍然认为您需要覆盖采用 id 字符串并将字符串转换为 guid 的 UserStore 方法。
【讨论】:
以上是关于如何使 EF-Core 使用 Guid 而不是 String 作为其 ID/主键的主要内容,如果未能解决你的问题,请参考以下文章
XCode 在模拟器选择中使用 GUID 而不是 iOS 版本号
使用 REST api 的 Guid 令牌而不是用户名/密码的基本身份验证
使用 REST api 的 Guid 令牌而不是用户名/密码的基本身份验证
如何使 Entity Framework 6 (DB First) 显式插入 Guid/UniqueIdentifier 主键?