我正在尝试对一个IUserStore
进行非常简单的实现,该基本上是:
- 使用冬眠
- 将用户保存在单个表中(无声明/登录名)
- 将角色名称存储在同一表中的 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>
类,但没有发现这种奇怪行为的明显原因。
可能是什么原因造成的?
您的异步方法目前从根本上被破坏了,因为您为所有操作返回相同的任务......并且从不启动它。我在这里没有看到任何"无限循环"——我只是看到你阻止了一个永远无法完成的任务。
目前还不清楚您希望通过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);
}
这将同步保存(就像您当前的代码一样),但随后返回一个已完成的任务(而不是根据您当前的代码永远不会完成的任务)。