How to make EF-Core use a Guid instead of String for its ID/Primary key

廉价感情. 提交于 2019-11-27 18:50:15
tmg

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

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();

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!