为了使这个问题更简单,我将描述更高级别的问题,然后在需要时详细介绍任何实现细节。

我在开发中的应用程序中使用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/

10-12 06:47