ASP.NET Identity 的 UserManager 缓存用户?

ASP.NET Identity's UserManager caches users?

我们将 ASP.NET Identity 2.2 与 ASP.NET MVC 5.2、Entity Framework 6.2 和 Unity 5.7 一起使用。

我们有一个 class、ConnectUserManager,它源自 ASP.NET 身份的 UserManager。每次都会传一个新构造的UserStoreUserManager

ConnectUserManager(以及 UserManager)的生命周期是每个请求:

Container.RegisterType<ConnectUserManager>(
    new PerRequestLifetimeManager(),
    new InjectionConstructor(
        Container.Resolve<ConnectDbContext>(),
        Container.Resolve<ITemplateManager>(),
        Settings.MaxFailedAccessAttemptsBeforeLockout,
        Settings.AccountLockoutTimeSpan));

当我们需要显示给定用户的详细信息时,控制器操作会这样检索用户:

public async Task<ActionResult> Details(int id)
{
    var user = await UserManager.FindByIdAsync(id);
    ...
}

其中 UserManager 是注入的 属性:

[Dependency]
public ConnectUserManager UserManager { get; set; }

问题是 user 似乎来自缓存:数据库中的修改似乎对我们的应用程序显示的内容没有任何影响。

这段代码已经投入生产一年了,我们从来没有遇到过任何问题:当我们的代码修改用户时,缓存似乎正确地失效了。

我们现在才注意到这个问题,因为当 Identity 锁定用户时,它会更新用户的 LockoutEndDateUtc 属性 但似乎没有使缓存失效,我们得到一个陈旧的 LockoutEndDateUtc我们显示的值。

我们做错了什么?

编辑:

@DotNetMatt 在评论中链接了以下问题:
Entity Framework caching in aspnet Identity.

除非我遗漏了什么,否则公认的解决方案(无论如何在撰写本文时)似乎与原始海报和我的问题完全无关。

不过楼主好像自己找到了(a?)解:"What I did was implemented my own userstore and manually accessed EF and used .AsNoTracking() to avoid the caching."

有没有什么方法可以做到这一点而不必重新实现(或子class)用户存储?

这是EF侥幸。 Identity 没有任何内置缓存。 我怀疑 ConnectDbContext 的生命周期是每个依赖项,而不是每个请求。

所以发生的是 ConnectDbContext returns 你的实例与实例 o ApplicationUser。然后 ConnectDbContext 的另一个实例进行锁定。但是当你回到 ConnectDbContext 的第一个实例时,它不知道任何关于由其他东西完成的更新。因此,当您第二次获得相同的实例时,它不会进入数据库,它 returns 您已经跟踪了该用户的实例。

解决此问题的方法 - 确保您的生命周期范围与所有涉及的移动部分相匹配:ConnectUserManagerUserStoreConnectDbContext。因此,每当您从数据库中获取对象时,总是 ConnectDbContext 的同一个实例为您提供此对象。

还有你 link 的答案 - 查看最后一条评论,OP 说他有同样的范围界定问题。

我发现了我的问题。 @trailmax 是正确的(在对他自己的回答的评论中)我周围有俘虏依赖。

该错误实际上出现在我的问题的代码片段中,该代码片段配置了 ConnectUserManager 依赖项的注入:

Container.RegisterType<ConnectUserManager>(
    new PerRequestLifetimeManager(),
    new InjectionConstructor(
        Container.Resolve<ConnectDbContext>(),
        Container.Resolve<ITemplateManager>(),
        Settings.MaxFailedAccessAttemptsBeforeLockout,
        Settings.AccountLockoutTimeSpan));

这解决了 ConnectDbContext 依赖关系 一次,此时此地,与以下版本不同:

Container.RegisterType<ConnectUserManager>(
    new PerRequestLifetimeManager(),
    new InjectionFactory(
        container => new ConnectUserManager(
            container.Resolve<ConnectDbContext>(),
            container.Resolve<ITemplateManager>(),
            Settings.MaxFailedAccessAttemptsBeforeLockout,
            Settings.AccountLockoutTimeSpan)));

每次 都会正确解析依赖关系 构建新的 ConnectUserManager