2

When i cache an object to the HttpContext Cache, my object disappear on the next read and I must reinsert the object a second time (not always but 90% of the time). After that, the object stay there without any problem.

Here's what happen:

  1. Start debugging the mvc project
  2. Read the age from the cache
  3. The age is null so i put 50 in the variable and then insert it in the cache
  4. The CacheItemRemovedCallback gets executed immediately when the response to the client is done. The CacheItemRemovedReason value is Removed
  5. The user click refresh immediately
  6. Read the age from the cache
  7. The age is still null so i put 50 in the variable and then insert it in the cache
  8. The user click refresh immediately
  9. Read the age from the cache
  10. The age is there finally!

So why the Cache have this problem to keep the object in cache on the first insert?

This behavior exist in the .Net framework 3.5, 4.0, 4.5, 4.5.2.

Here's the code:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        int? age = this.HttpContext.Cache.Get("age") as int?;
        if (age == null)
        {
            age = 50;

            this.HttpContext.Cache.Add("age", age, null, DateTime.Now.AddHours(5), TimeSpan.Zero, CacheItemPriority.Default, new CacheItemRemovedCallback(this.CacheItemRemovedCallback));
        }
        return View();
    }

    public void CacheItemRemovedCallback(String key, Object value, CacheItemRemovedReason reason)
    {
    }
}
Alexandre Jobin
  • 2,811
  • 4
  • 33
  • 43
  • Is there any reason why you arent specifying a cache duration in the Insert()? The item may be getting scavenged sooner than you think. – StingyJack Jan 30 '15 at 19:18
  • Because this is not required to do so and since that the second time the object is cached for good, i understand that whithout specifying the cache duration, the default is to keep the object infinitly. – Alexandre Jobin Jan 30 '15 at 19:28
  • 1
    Cache is never permanent. Infinity is not the default and cache will have items removed if they are deemed no longer needed. Supply a time of 1 hour for the cache and you should see the item the first time you expect it. – StingyJack Jan 30 '15 at 19:36
  • sure you dont have a race condition? – AK_ Jan 30 '15 at 19:54
  • For reference [httpcontext-current-cache-item-null-after-page-refresh](http://stackoverflow.com/questions/14421744/httpcontext-current-cache-item-null-after-page-refresh) – Jason Evans Jan 30 '15 at 19:59
  • ***I cannot replicate the issue*** in all browsers - IE, FF and Chrome. Could you upload the sample project somewhere? – Win Jan 30 '15 at 20:01
  • I have updated the description. I have added the CacheItemRemovedCallback function and it gets executed immediately when i insert the value for the first time. – Alexandre Jobin Jan 31 '15 at 19:21
  • I've just tested the same code at home and I cannot reproduce the problem. Pretty weird. Is there a way to listen to some other events of the application to know why my object gets removed from the cache? – Alexandre Jobin Jan 31 '15 at 19:29

1 Answers1

3

The reason why my cached item was removed immediately after his insertion was because the AppDomain get unloaded after the first call to the website. By catching the unload event of the AppDomain, i've been able to know the shutdown reason. The antivirus was scanning a file of the website that triggered the FileChangesMonitor event of the AppDomain that then triggered the unload of the AppDomain.

Here's how to detect the reason why the AppDomain get unloaded :

Global.asax

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.DomainUnload += DomainUnloadEventHandler;
    }

    static void DomainUnloadEventHandler(object sender, EventArgs e)
    {
        var httpRuntimeType = typeof(HttpRuntime);

        var httpRuntime = httpRuntimeType.InvokeMember(
            "_theRuntime",
            BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField,
            null, null, null) as HttpRuntime;

        var shutDownMessage = httpRuntimeType.InvokeMember(
            "_shutDownMessage",
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
            null, httpRuntime, null) as string;

        string shutDownStack = httpRuntime.GetType().InvokeMember(
            "_shutDownStack",
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
            null, httpRuntime, null) as string;
    }
}

Here's what the shutDownMessage variable contains:

_shutDownMessage: Change Notification for critical directories.
bin dir change or directory rename
HostingEnvironment initiated shutdown
HostingEnvironment caused shutdown
Change in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\48a6542f\e317ebf6\hash\hash.web

Has you can see, the hash.web file is the cause of the AppDomain unload. Now, who is altering this file? It turns out to be the antivirus. By deactivating the On-Access scanner of McAfee, the hash.web file was not altered anymore so no AppDomain unload. Problem solved!

For further information, you can read this blog post.

Alexandre Jobin
  • 2,811
  • 4
  • 33
  • 43