为了使这个问题更简单,我将描述更高级别的问题,然后在需要时详细介绍任何实现细节。
我在开发中的应用程序中使用ASP.NET Identity。在一系列请求的特定情况下,UserManager首先获取当前用户(至少一个FindById请求),并在该用户处获取该用户。在后续请求中,我更新了由UserManager.Update保存的有关此用户的信息,并且可以看到更改持久存在于数据库中。
问题在于,在进一步的后续请求中,从FindById获得的用户对象不会更新。这很奇怪,但可能与我不了解的UserManager中的缓存有关。但是,当我跟踪数据库调用时,我看到UserManager确实在将sql请求发送到数据库以获取用户。
这就是真正令人奇怪的地方-即使确认数据库是最新的,UserManager仍会以某种方式从该过程中返回一个旧对象。当我自己运行直接追溯到数据库的完全相同的查询时,我将按预期获得更新的数据。
这是什么黑魔法?
显然,某些内容被缓存在某个地方,但是为什么要对数据库进行查询,而只是忽略它获取的更新数据?
示例
下面的示例为 Controller 操作的每个请求按预期更新了数据库中的所有内容,并且当GetUserDummyTestClass在UserManager的另一个实例上调用findById时,我可以跟踪sql请求,并可以将这些请求直接测试到db并验证它们是否返回更新的数据。但是,从同一行代码返回的用户对象仍然具有旧值(在这种情况下,无论应用程序调用了Test操作多少次,启动应用程序之后的第一次编辑)。
控制者
public ActionResult Test()
{
var userId = User.Identity.GetUserId();
var user = UserManager.FindById(userId);
user.RealName = "name - " + DateTime.Now.ToString("mm:ss:fff");
UserManager.Update(user);
return View((object)userId);
}
Test.cshtml
@model string
@{
var user = GetUserDummyTestClass.GetUser(Model);
}
@user.RealName;
GetUserDummyTestClass
public class GetUserDummyTestClass
{
private static UserManager<ApplicationUser> _userManager;
private static UserManager<ApplicationUser> UserManager
{
get { return _userManager ?? (_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))); }
}
public static ApplicationUser GetUser(string id)
{
var user = UserManager.FindById(id);
return user;
}
}
更新
正如Erik指出的那样,我不应该使用静态UserManager。但是,如果我将GetManagerDummyTest中的UserManager绑定(bind)到HttpContext(每个HttpRequest坚持使用),以防在请求期间多次使用它,它仍会缓存它通过ID获得的第一个User对象,并且不考虑任何更新从另一个UserManager。因此,暗示真正的问题确实是,我按照Trailmax的建议使用了两个不同的UserManager,并且它不是针对这种用法而设计的。
在上面的示例中,如果我将UserManager的GetManagerDummyTestClass保持在HttpRequest之上,请添加Update方法并仅在 Controller 中使用它,否则一切都会按预期进行。
因此,如果要得出一个结论,是否正确声明:如果我想使用 Controller 范围之外的UserManager的逻辑,则必须在适当的类中全局化UserManager实例,在该类中我可以将该实例绑定(bind)到HttpContext,是否要避免一次性创建和处置实例?
更新2
经过进一步的调查,我意识到我确实打算在每个请求中使用一个实例,并且实际上已经在Startup.Auth中为OwinContext设置了该实例,然后按以下方式进行访问:
using Microsoft.AspNet.Identity.Owin;
// Controller
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()
// Other scopes
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()
从所提供的默认AccountController的设置来看,这实际上是令人尴尬的显而易见,但我想上面描述的相当奇怪和出乎意料的行为证明确实令人分心。尽管如此,即使使用OwinContext.GetUserManager不再是问题,了解这种现象的原因还是很有趣的。
最佳答案
您的问题是您使用的是两个不同的UserManager实例,而且看起来它们都是静态定义的(在Web应用程序中这是一个很大的禁忌,因为它们在系统的所有线程和用户之间共享,而并非线程安全,因为它们包含特定于用户的状态,您甚至无法通过锁定它们来使它们成为线程安全的)
将您的GetUserDummyTestClass更改为此:
private static UserManager<ApplicationUser> UserManager
{
get { return new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext())); }
}
public static ApplicationUser GetUser(string id)
{
using (var userManager = UserManager)
{
return UserManager.FindById(id);
}
}
关于c# - .Net Identity中UserManager的奇怪行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25701942/