We're using ASP.NET Identity 2.2 with ASP.NET MVC 5.2, Entity Framework 6.2 and Unity 5.7.
We have a class, ConnectUserManager
, that derives from ASP.NET Identity's UserManager
. A newly constructed UserStore
is passed to UserManager
every time.
The lifetime of ConnectUserManager
(and thus of UserManager
) is per request:
Container.RegisterType<ConnectUserManager>(
new PerRequestLifetimeManager(),
new InjectionConstructor(
Container.Resolve<ConnectDbContext>(),
Container.Resolve<ITemplateManager>(),
Settings.MaxFailedAccessAttemptsBeforeLockout,
Settings.AccountLockoutTimeSpan));
When we need to display the details for a given user, a controller action retrieves the user as such:
public async Task<ActionResult> Details(int id)
{
var user = await UserManager.FindByIdAsync(id);
...
}
where UserManager
is an injected property:
[Dependency]
public ConnectUserManager UserManager { get; set; }
The problem is that user
appears to come from a cache: modifications in the database don't appear to have any effect on what our application displays.
This code has been in production for a year and we never had any issue with it: the cache seems properly invalidated when our code modifies a user.
We only noticed the problem now because when Identity locks a user out, it updates the user's LockoutEndDateUtc
property but seemingly without invalidating the cache, and we get a stale LockoutEndDateUtc
value in our display.
What are we doing wrong?
EDIT:
@DotNetMatt linked the following question in a comment:
Entity Framework caching in aspnet Identity.
Unless I'm missing something, the accepted solution (at the time of writing anyway) seems completely unrelated to the original poster's and to my problem.
However, the original poster seems to have found the (a?) solution by himself: "What I did was implemented my own userstore and manually accessed EF and used .AsNoTracking() to avoid the caching."
Is there any way to do this without having to reimplement (or subclass) the user store?
This is EF fluke. Identity does not have any inbuilt caching.
I suspect your lifetimescope for ConnectDbContext
is per depedency, not per request.
So what happens is once instance of ConnectDbContext
returns you with instance o ApplicationUser
. Then another instance of ConnectDbContext
does the lockout. But then when you go back to the first instance of ConnectDbContext
, it does not know anything about the update that has been done by something else. Hence when you get the same instance second time, it does not go into the database, it returns you already tracked instance of this user.
Way to fix this - make sure your lifetime scopes match for all the moving parts involved: ConnectUserManager
,UserStore
and ConnectDbContext
. So whenever you get objects form DB, it is always the same instance of ConnectDbContext
that is providing this for you.
Also the answer you link to - see the very last comment, OP says he has the same scoping issue.
I found my problem. @trailmax was correct (in comments to his own answer) that I had captive dependencies lying around.
The bug was actually in the snippet of code from my question that configures injection of ConnectUserManager
dependencies:
Container.RegisterType<ConnectUserManager>(
new PerRequestLifetimeManager(),
new InjectionConstructor(
Container.Resolve<ConnectDbContext>(),
Container.Resolve<ITemplateManager>(),
Settings.MaxFailedAccessAttemptsBeforeLockout,
Settings.AccountLockoutTimeSpan));
This resolves ConnectDbContext
dependencies once, here and now, unlike the following version:
Container.RegisterType<ConnectUserManager>(
new PerRequestLifetimeManager(),
new InjectionFactory(
container => new ConnectUserManager(
container.Resolve<ConnectDbContext>(),
container.Resolve<ITemplateManager>(),
Settings.MaxFailedAccessAttemptsBeforeLockout,
Settings.AccountLockoutTimeSpan)));
which correctly resolves dependencies every time a new ConnectUserManager
is constructed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With