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
。每次都会传一个新构造的UserStore
给UserManager
。
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 您已经跟踪了该用户的实例。
解决此问题的方法 - 确保您的生命周期范围与所有涉及的移动部分相匹配:ConnectUserManager
、UserStore
和 ConnectDbContext
。因此,每当您从数据库中获取对象时,总是 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
。
我们将 ASP.NET Identity 2.2 与 ASP.NET MVC 5.2、Entity Framework 6.2 和 Unity 5.7 一起使用。
我们有一个 class、ConnectUserManager
,它源自 ASP.NET 身份的 UserManager
。每次都会传一个新构造的UserStore
给UserManager
。
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 您已经跟踪了该用户的实例。
解决此问题的方法 - 确保您的生命周期范围与所有涉及的移动部分相匹配:ConnectUserManager
、UserStore
和 ConnectDbContext
。因此,每当您从数据库中获取对象时,总是 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
。