Reputation: 6863
I'm facing a strange problem with ASP.NET MemoryCaching in a MVC 3 ASP.NET application.
Each time an action is executed, I check if its LoginInfo are actually stored in the MemoryCache (code has been simplified, but core is as follow):
[NonAction]
protected override void OnAuthorization(AuthorizationContext filterContext) {
Boolean autorizzato = false;
LoginInfo me = CacheUtils.GetLoginData(User.Identity.Name);
if (me == null)
{
me = LoginData.UserLogin(User.Identity.Name);
CacheUtils.SetLoginInfo(User.Identity.Name, me);
}
// Test if the object is really in the memory cache
if (CacheUtils.GetLoginData(User.Identity.Name) == null) {
throw new Exception("IMPOSSIBLE");
}
}
The GetLoginInfo is:
public static LoginInfo GetLoginData(String Username)
{
LoginInfo local = null;
ObjectCache cache = MemoryCache.Default;
if (cache.Contains(Username.ToUpper()))
{
local = (LoginInfo)cache.Get(Username.ToUpper());
}
else
{
log.Warn("User " + Username + " not found in cache");
}
return local;
}
The SetLoginInfo is:
public static void SetLoginInfo (String Username, LoginInfo Info)
{
ObjectCache cache = MemoryCache.Default;
if ((Username != null) && (Info != null))
{
if (cache.Contains(Username.ToUpper()))
{
cache.Remove(Username.ToUpper());
}
cache.Add(Username.ToUpper(), Info, new CacheItemPolicy());
}
else
{
log.Error("NotFound...");
}
}
The code is pretty straightforward, but sometimes (totally randomly), just after adding the LoginInfo to the MemoryCache, this results empty, the just added Object is not present, therefore I got the Exception.
I'm testing this both on Cassini and IIS 7, it seems not related to AppPool reusability (enabled in IIS 7), I've tested with several Caching policies, but cannot make it work
What Am I missing/Failing ?
PS: forgive me for my bad english
Upvotes: 10
Views: 6045
Reputation: 9934
I believe you are running into a problem that Scott Hanselman identified as a .NET 4 bug. Please see here: MemoryCache Empty : Returns null after being set
Upvotes: 1
Reputation: 2800
Looking at the code for MemoryCache using a decomplier there is the following private function
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs eventArgs)
{
if (!eventArgs.IsTerminating)
return;
this.Dispose();
}
There is an unhandled exception handler setup by every MemoryCache for the current domain Thread.GetDomain()
so if there is ever any exception in your application that is not caught which may be common in a website it disposes the MemoryCache for ever and cannot be reused this is especially relevant for IIS apps as apposed to windows applications that just exit on unhanded exceptions.
Upvotes: 3
Reputation: 361
The MemoryCache has limited size. For the Default instance, is't heuristic value (according to MSDN).
Have you tried to set Priority property on CacheItemPolicy instance to NotRemovable?
You can have race-condition because the Contains-Remove-Add sequence in SetLoginInfo is not atomic - try to use Set method instead.
Btw. you are working on web application so why not to use System.Web.Caching.Cache instead?
Upvotes: 2