.net Core Identity w/o EntityFramework 使用 int id

.net Core Identity w/o EntityFramework using int ids

我正在尝试使用 Identity w/o EntityFramework 创建一个 .net Core 解决方案,但使用整数 ID(UserId 和 RoleId)。我已按照此基本设置创建解决方案并删除 EF (https://taherchhabra.github.io/jekyll/update/2016/09/22/aspnet-core-identity-without-entityframework.html),但这仍然有用户的愚蠢 Guid id 列。当我尝试更改它时,我 运行 在创建 UserStore 时遇到了问题,因为它继承自 IUserStore<User>,这需要我实现

Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken)

which or course 表示 Id 值是字符串而不是 int。

我读过几篇关于如何更改基于 EF 的解决方案以使用整数的文章,我深入研究了 EF 是如何做到这一点的,但它都是基于使用 TKey 的 EF 类 .

我尝试创建自己的 IUserStore 以将字符串更改为整数,但该项目甚至无法启动(它可以正常编译但是当我尝试 运行 它在本地它会在完成启动之前退出向上)。

有谁知道如何做到这一点?我搜索了很多,但找不到任何东西。

编辑: 到目前为止我所做的是创建我自己的 UserStore 和 RollStore(它们基本上只是原件的副本,一旦我开始工作我将对其进行调整)。在我的 HomeController 中,我添加了以下内容:

public class HomeController : Controller
{
    private readonly UserManager<models.User> _userManager;
    public HomeController(UserManager<models.User> userManager)
    {
      _userManager = userManager;
    }

    public async Task<IActionResult> Index()
    {
        var user = new models.User();
        if(User.Identity.IsAuthenticated)
            user = await _userManager.FindByNameAsync(User.Identity.Name);

        return View();
    }

}

这基本上是一个 "is it plugged in" 测试,我可以在调试器中单步执行。

在我的 Startup.cs:

services.AddSingleton<IUserStore<User>, UserStore>();
services.AddSingleton<IRoleStore<Role>, RoleStore>();

services.AddIdentity<User, Role>()
    .AddDefaultTokenProviders();

这确实有效(通过我的端点进行调试,user 已正确填充)。但是,如果仔细观察,我会意识到 IUserStoreIRoleStoreMicrosoft.AspNetCore.Identity 版本,这意味着我仍然依赖于这些合同并且需要使用 Guid 作为 Id。

如果我强制这个问题,使用

 services.AddSingleton<SandboxCoreDapper.Identity.Interfaces.IUserStore<User>, UserStore>();
 services.AddSingleton<SandboxCoreDapper.Identity.Interfaces.IRoleStore<Role>, RoleStore>();

然后当我尝试访问我的端点时,UserManager 将无法解析:

"Unable to resolve service for type 'Microsoft.AspNetCore.Identity.IUserStore1[SandboxCoreDapper.Models.User]' while attempting to activate 'Microsoft.AspNetCore.Identity.UserManager1[SandboxCoreDapper.Models.User]'."

编辑 2 我发现这个有趣的讨论:https://github.com/aspnet/Identity/issues/493

在此基础上,我继续创建了自己的 RoleManagerUserManager,它们基本上只是扩展了 Identity 版本:

namespace SandboxCoreDapper.Identity.Services
{

    public class UserManager<TUser> : Microsoft.AspNetCore.Identity.UserManager<TUser> where TUser : class
    {

        public UserManager(
                sandyId.IUserStore<TUser> store, // my version of IUserStore
                IOptions<IdentityOptions> optionsAccessor, 
                IPasswordHasher<TUser> passwordHasher, 
                IEnumerable<IUserValidator<TUser>> userValidators, 
                IEnumerable<IPasswordValidator<TUser>> passwordValidators, 
                ILookupNormalizer keyNormalizer, 
                IdentityErrorDescriber errors, 
                IServiceProvider services, 
                ILogger<UserManager<TUser>> logger)
            :base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
        {

        }

    }
}  

RoleManager.

也是如此

然后我添加到Startup.cs

services.AddIdentity<User, Role>()
     .AddDefaultTokenProviders()
     .AddUserManager<SandboxCoreDapper.Identity.Services.UserManager<User>>()
     .AddRoleManager<SandboxCoreDapper.Identity.Services.RoleManager<Role>>()
            ;

目前一切顺利...

这是对我的评论的回应,你的 question/comment:

只需声明您自己的 UserStore,然后将其注册到 Startup.cs。

这是我为适应这种情况而实现的 UserStore:

public class UserStore : IUserPasswordStore<Login>, IUserEmailStore<Login>, IUserPhoneNumberStore<Login>
    {
        private readonly IAccountRepo _accountRepo;

        public UserStore(IAccountRepo accountRepo)
        {
            _accountRepo = accountRepo;
        }

        public void Dispose()
        {
        }

        public async Task<string> GetUserIdAsync(Login user, CancellationToken cancellationToken)
        {
            return await Task.FromResult(user.Username);
        }

        public async Task<string> GetUserNameAsync(Login user, CancellationToken cancellationToken)
        {
            return await Task.FromResult(user.Username);
        }

        public Task SetUserNameAsync(Login user, string userName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetNormalizedUserNameAsync(Login user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task SetNormalizedUserNameAsync(Login user, string normalizedName, CancellationToken cancellationToken)
        {
            await Task.FromResult(user.Username = normalizedName.ToLower());
        }

        public async Task<IdentityResult> CreateAsync(Login user, CancellationToken cancellationToken)
        {
            await _accountRepo.CreateLogin(user);
            return IdentityResult.Success;
        }

        public async Task<IdentityResult> UpdateAsync(Login user, CancellationToken cancellationToken)
        {
            await _accountRepo.UpdateLogin(user.LoginId, user.Email, true);
            return IdentityResult.Success;
        }

        public Task<IdentityResult> DeleteAsync(Login user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<Login> FindByIdAsync(string userId, CancellationToken cancellationToken)
        {
            return await _accountRepo.GetUser(userId);
        }

        public async Task<Login> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
        {
            return await _accountRepo.GetUser(normalizedUserName);
        }

        public async Task SetPasswordHashAsync(Login user, string passwordHash, CancellationToken cancellationToken)
        {
            if (user.LoginId != 0)
            {
                await _accountRepo.ChangePassword(user.Email, user.Username, passwordHash);
            }

            user.PasswordHash = passwordHash;
        }

        public async Task<string> GetPasswordHashAsync(Login user, CancellationToken cancellationToken)
        {
            return await Task.FromResult(user.PasswordHash);
        }

        public async Task<bool> HasPasswordAsync(Login user, CancellationToken cancellationToken)
        {
            return await Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash) && !string.IsNullOrEmpty(user.Salt));
        }

        public Task SetEmailAsync(Login user, string email, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetEmailAsync(Login user, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.Email);
        }

        public async Task<bool> GetEmailConfirmedAsync(Login user, CancellationToken cancellationToken)
        {
            return await Task.FromResult(user.EmailConfirmed);
        }

        public Task SetEmailConfirmedAsync(Login user, bool confirmed, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.EmailConfirmed = confirmed);
        }

        public Task<Login> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetNormalizedEmailAsync(Login user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetNormalizedEmailAsync(Login user, string normalizedEmail, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.Email = normalizedEmail.ToLower());
        }

        public Task SetPhoneNumberAsync(Login user, string phoneNumber, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetPhoneNumberAsync(Login user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<bool> GetPhoneNumberConfirmedAsync(Login user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetPhoneNumberConfirmedAsync(Login user, bool confirmed, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

以及 Startup.cs ConfigureServices 方法:

services.AddSingleton<IUserStore<Login>, UserStore>();

我最接近的答案是这个 git 项目:https://github.com/grandchamp/Identity.Dapper

它仍在进行中,我正在与项目所有者合作进行一些改进,但对于遇到这个问题的其他人来说,现在该项目是你最好的选择。