如何使EF-Core为其ID /主键使用Guid而不是String

时间:2016-05-11 14:46:02

标签: asp.net-core asp.net-identity entity-framework-core asp.net-identity-3

当我查看ASP.NET 3 Identity时,它使用string而不是Guid作为唯一主键。

在我的Entity Framework code first用户'ApplicationUser班级中,我继承了Identity class

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如何将String转换为Guid,但我希望数据库表Id为Guid。

当长度为nvarchar(128)时,我发现了另一个question以及之前的BETA。给出的理由是并非所有数据库都支持Guids,并且它的灵活性也发生了变化。

是否必须有一种简单的方法可以在不重写整个identity 3的情况下从字符串更改为Guid?

在创建SQL Server数据库约束时,

nvarchar(450)真的有点过分并会发出各种警告。数据库管理员肯定不会喜欢这些警告。

3 个答案:

答案 0 :(得分:39)

您需要从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 Service添加到像这样的容器

services.AddIdentity<ApplicationUser, Role>()
        .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
        .AddDefaultTokenProviders()
        .AddUserStore<UserStore<ApplicationUser, Role, ApplicationDbContext, Guid>> ()
        .AddRoleStore<RoleStore<Role, ApplicationDbContext, Guid>>();

如果尚未创建数据库,请清除迁移文件夹并运行ef命令

答案 1 :(得分:9)

我还没有搞过这个例子的迁移(他们可能需要一些调整),但是在这方面,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();

答案 2 :(得分:2)

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字符串的UserStore方法并将字符串转换为guid。