UserManager.CreateAsync(user, password) stuck in infinite loop

I'm trying to make a very simple implementation of an IUserStore that would essentially:

  • use NHibernate
  • save users in a single table (no claims/logins)
  • store role names in the same table, in an nvarchar column (pipe ('|') separated if multiple items).

When I run the following method:

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

the last line would never finish executing.

The weird thing is that the UserManager never calls any of the functions in my SimpleUserStore; it gets stuck before that.

Here are the components I defined:

The user class:

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

The User Store:

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

How I manage roles

Probably not so important, but here it is anyway:

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

I have been inspecting the UserManager<TUser> class with DotPeek, but found no obvious causes for this weird behavior.

What could be causing this?

Jon Skeet
people
quotationmark

Your approach to async is fundamentally broken at the moment, because you're returning the same task for all operations... and never starting it. I don't see any "infinite loop" here - I just see you blocking on a task which can never complete.

It's not clear what you hope to accomplish with your EmptyTask task, but it's definitely not helping you at the moment.

Furthermore, it's not clear that your code is really asynchronous in any aspect, unless _session.Save (etc) are really asynchronous.

You could improve things somewhat by just running extra tasks, e.g.

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

... although the fact that you're then immediately blocking on the task in the calling code makes it pointless, too. (It's not even clear how this compiles at the moment, as Task doesn't have a Result property... only Task<T> does.)

As noted in comments, this will create a new task which will effectively run synchronously in a new thread - using more threads than you'd otherwise need, with the potential benefit of parallelism. It doesn't achieve the same goals as a properly asynchronous database call (where there wouldn't be any threads tied up for the save call - just a task which would complete when the relevant network response was returned).

If you don't really care about it being asynchronous, you can use:

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

This will synchronously save (just like your current code does) but then return a completed task (rather than the task which will never complete, as per your current code).

people

See more on this question at Stackoverflow