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

时间:2014-03-06 10:19:08

标签: c# task-parallel-library async-await asp.net-identity

我正在尝试一个非常简单的IUserStore实现,基本上是:

  • 使用NHibernate
  • 将用户保存在一个表中(无索赔/登录)
  • 将角色名称存储在nvarchar列(管道('|')中的同一个表中 如果有多个项目则分开。)

当我运行以下方法时:

[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...
    }
}

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

奇怪的是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(); }
    }
}

用户商店:

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

我如何管理角色

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

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

我一直在用DotPeek检查UserManager<TUser>类,但没有找到导致这种奇怪行为的明显原因。

导致这种情况的原因是什么?

1 个答案:

答案 0 :(得分:9)

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

目前尚不清楚您希望通过EmptyTask任务完成什么,但目前肯定 无法帮助您。

此外,除非_session.Save(等)实际上是异步的,否则您的代码在任何方面都不是真正的异步并不清楚。

你可以通过运行额外的任务来改善某些,例如

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

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

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

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

public Task CreateAsync(TUser user)
{
    _session.Save(user);
    return Task.FromResult<object>(null);
}

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