UserManager.CreateAsync(用户,密码)陷入无限循环

Cri*_*scu 1 c# task-parallel-library async-await asp.net-identity

我试图做一个非常简单的实现,IUserStore基本上:

  • 使用NHibernate
  • 将用户保存在单个表中(无索赔/登录)
  • 将角色名称存储在同一个表中的nvarchar列中('|'如果有多个项目,则为pipe()).

当我运行以下方法时:

[Fact]
public void Create_A_User()
{
    // _session is a valid NHibernate ISession object
    using (var userStore = new SimpleUserStore<SimpleIdentityUser>(_session))
    using (var userManager = new UserManager<SimpleIdentityUser>(userStore))
    {
        var user = new SimpleIdentityUser
        {
            UserName = "kenny_mccormick",
            RolesStr = "admin",
        };

        var createTask = userManager.CreateAsync(user, "the_password");

        var result = createTask.Result; // this never finishes...
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一行永远不会完成执行.

奇怪的是,UserManager永远不会调用我的任何功能SimpleUserStore; 它在那之前就被卡住了.

以下是我定义的组件:

用户类:

public class SimpleIdentityUser : IUser
{
    public virtual Guid UserId { get; set; }
    public virtual string PasswordHash { get; set; }
    public virtual string SecurityStamp { get; set; }
    public virtual string RolesStr { get; set; }
    public virtual string UserName { get; set; }

    public virtual string Id
    {
        get { return UserId.ToString(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

用户商店:

public class SimpleUserStore<TUser> :
    IUserPasswordStore<TUser>,
    IUserRoleStore<TUser>,
    IUserSecurityStampStore<TUser>
    where TUser : SimpleIdentityUser
{
    // ReSharper disable once StaticFieldInGenericType
    private static readonly Task EmptyTask = new Task(() => { });

    private readonly ISession _session;

    public SimpleUserStore(ISession session)
    {
        _session = session;
    }

    public Task<TUser> FindAsync(UserLoginInfo login)
    {
        return Task.FromResult((TUser) null);
    }

    public Task CreateAsync(TUser user)
    {
        _session.Save(user);
        return EmptyTask;
    }

    public Task UpdateAsync(TUser user)
    {
        // updates will (hopefully) be saved automatically when the current session is committed
        return EmptyTask;
    }

    public Task DeleteAsync(TUser user)
    {
        _session.Delete(user);
        return EmptyTask;
    }

    public Task<TUser> FindByIdAsync(string userId)
    {
        TUser user = null;
        Guid guidId;

        if (Guid.TryParse(userId, out guidId))
            user = _session.Get<TUser>(guidId);

        return Task.FromResult(user);
    }

    public Task<TUser> FindByNameAsync(string userName)
    {
        TUser user = _session.Query<TUser>().SingleOrDefault(u => u.UserName == userName);
        return Task.FromResult(user);
    }

    public Task SetPasswordHashAsync(TUser user, string passwordHash)
    {
        user.PasswordHash = passwordHash;
        return EmptyTask;
    }

    public Task<string> GetPasswordHashAsync(TUser user)
    {
        return Task.FromResult(user.PasswordHash);
    }

    public Task<bool> HasPasswordAsync(TUser user)
    {
        return Task.FromResult(user.PasswordHash != null);
    }

    public void Dispose()
    {
    }

    public Task AddToRoleAsync(TUser user, string role)
    {
        new SimpleRoleManager<TUser>(user).AddRole(role);

        return EmptyTask;
    }

    public Task RemoveFromRoleAsync(TUser user, string role)
    {
        new SimpleRoleManager<TUser>(user).DeleteRole(role);

        return EmptyTask;
    }

    public Task<IList<string>> GetRolesAsync(TUser user)
    {
        List<string> roles = new SimpleRoleManager<TUser>(user).GetRoles().ToList();

        return Task.FromResult((IList<string>) roles);
    }

    public Task<bool> IsInRoleAsync(TUser user, string role)
    {
        return Task.FromResult(new SimpleRoleManager<TUser>(user).IsInRole(role));
    }

    public Task SetSecurityStampAsync(TUser user, string stamp)
    {
        user.SecurityStamp = stamp;
        return EmptyTask;
    }

    public Task<string> GetSecurityStampAsync(TUser user)
    {
        return Task.FromResult(user.SecurityStamp);
    }
}
Run Code Online (Sandbox Code Playgroud)

我如何管理角色

可能不那么重要,但无论如何它在这里:

public class SimpleRoleManager<TUser> where TUser : SimpleIdentityUser
{
    private const string Separator = "|";

    private readonly TUser _user;

    public SimpleRoleManager(TUser user)
    {
        _user = user;
    }

    public string[] GetRoles()
    {
        return (_user.RolesStr ?? String.Empty)
            .Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    }

    public bool IsInRole(string roleName)
    {
        return GetRoles().Contains(roleName);
    }

    public bool AddRole(string roleName)
    {
        var roles = GetRoles().ToList();

        if (roles.Contains(roleName))
            return false;

        roles.Add(roleName);
        SetRoles(roles);
        return true;
    }

    public bool DeleteRole(string roleName)
    {
        List<string> roles = GetRoles().ToList();

        if (!roles.Contains(roleName))
            return false;

        roles.Remove(roleName);
        SetRoles(roles);
        return true;
    }

    private void SetRoles(IEnumerable<string> roles)
    {
        _user.RolesStr = String.Join(Separator, roles);
    }
}
Run Code Online (Sandbox Code Playgroud)

我一直在UserManager<TUser>用DotPeek 检查课程,但没有发现这种奇怪行为的明显原因.

可能是什么导致了这个?

Jon*_*eet 9

你的异步方法从根本上打破了,因为你正在为所有操作返回相同的任务......而且从不启动它.我在这里看不到任何"无限循环" - 我只是看到你阻止了一项永远无法完成的任务.

目前还不清楚你希望用你的EmptyTask任务完成什么,但目前它肯定无法帮助你.

此外,不清楚你的代码在任何方面_session.Save都是异步的,除非(etc)真的是异步的.

你可以改善的事情有些通过只是运行额外的任务,例如,

public Task CreateAsync(TUser user)
{
    Action action = () => _session.Save(user);
    return Task.Run(action);
}
Run Code Online (Sandbox Code Playgroud)

...虽然事实上你立即阻止调用代码中的任务使得它也毫无意义.(目前还不清楚这是如何编译的,因为Task没有Result属性......只是Task<T>.)

正如评论中所指出的,这将创建一个新任务,它将在新线程中有效地同步运行 - 使用比您需要更多的线程,并获得并行性的潜在好处.它没有实现与正确异步数据库调用相同的目标(其中没有任何线程与保存调用相关联 - 只是在返回相关网络响应时完成的任务).

如果你真的不关心它是异步的,你可以使用:

public Task CreateAsync(TUser user)
{
    _session.Save(user);
    return Task.FromResult<object>(null);
}
Run Code Online (Sandbox Code Playgroud)

这将同步保存(就像您当前的代码一样),但随后返回一个已完成的任务(而不是根据您当前的代码永远不会完成的任务).