如何使 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 而不是 stringuniqueidentifier 而不是 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&lt;ApplicationUser, Role, TKey&gt; 并使用 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 但在设置上下文时遇到问题。【参考方案2】:

我还没有搞砸这个例子的迁移(他们可能需要一些调整),但是 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&lt;GuidDataContext, Guid&gt;() 来自哪里?我只有AddEntityFrameworkStores&lt;GuidDataContext&gt;(),没有将第二种类型传递给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 版本号

嵌套 tph 继承成员的 ef-core 负载收集属性

如何在 SQLite 中获取最后插入的行 guid?

使用 REST api 的 Guid 令牌而不是用户名/密码的基本身份验证

使用 REST api 的 Guid 令牌而不是用户名/密码的基本身份验证

如何使 Entity Framework 6 (DB First) 显式插入 Guid/UniqueIdentifier 主键?