5

I have a tool that works perfectly fine in Chrome and FF. But with any versions of IE the browser is displaying cache info over doing an Ajax pull to retrieve the data.

Here's my setup:

I have criterias that I loop through:

@foreach (var item in Model)
{
        <div class="sizeTDCriteria">
            @Html.DisplayFor(modelItem => item.Text)
        </div>
        <div class="sizeTDCriteriaAction">
            @Ajax.ImageActionLink(Url.Content("~/Content/images/icons/edit.png"), "Edit Criteria", "AddOrEditCriteria", "Management", new { competencySectionId = ViewBag.competencySectionId, criteriaId = item.Id }, new AjaxOptions { UpdateTargetId = "AddOrEditCriteriaFormContainer" }, new { @class = "iconPosition" })
            @Ajax.ImageActionLink(Url.Content("~/Content/images/icons/delete.png"), "Delete Criteria", "RemoveCriteria", "Management", new { criteriaId = item.Id }, new AjaxOptions { UpdateTargetId = "CompetenciesManagementWrapper" }, new { @class = "iconPosition" })
        </div>
}

The ImageActionLink is just a helper that creates a ActionLink with an image inside, I've tried doing this with a normal ActionLink and the issue occurs as well so you can ignore that. I've also tried to change the whole ImageActionLink by a plain <img> with a jQuery trigger with no difference.

What happens is that when a user clicks on the Edit link it will do an ajax call to the "AddOrEditCriteria", that ActionResult, finds the criteria, and displays the PartialView form back into the div "#AddOrEditCriteriaFormContainer". So far so good, this works fine in all browsers.

But when I click a second time on that edit, instead of doing the ajax call, IE simply displays the PartialView from what it had in the cache, when all other browsers correctly pull the data again (which is required, as that view allows to edit the criterias, it could have been edited by someone else in the mean time).

The weird part is that IE is making the call but somehow it never reaches the server, it just uses the cache by using a Result 304. You can see from this Network capture:

URL Method  Result  Type    Received    Taken   Initiator   Wait??  Start?? Request??   Response??  Cache read??    Gap??
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 304 text/html   182 B   < 1 ms  JS Library XMLHttpRequest
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 304 text/html   182 B   < 1 ms  JS Library XMLHttpRequest
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 200 text/html   1.53 KB 1.24 s  JS Library XMLHttpRequest

The last one is the first to happen, the first two were done after and are getting a 304 return.

I found a way to fix it by adding a "breakcache" parameter to the ajax call with a random number, but that just doesn't seem like a good solution.

This issue has been screwing our users because they see data that should is not updated all because of IE.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
LanFeusT
  • 2,392
  • 5
  • 38
  • 53

1 Answers1

11

IE will cache AJAX GETs unless you tell it not to.

You tell it not to via the Cache-Control header.

So here's what we do:

[CacheControl(HttpCacheability.NoCache), HttpGet]
public ActionResult MyAction() { return Json("Hi!", JsonRequestBehavior.AllowGet); }

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class CacheControlAttribute : ActionFilterAttribute
{
    public CacheControlAttribute(HttpCacheability cacheability)
    {
        this._cacheability = cacheability;
    }

    public HttpCacheability Cacheability { get { return this._cacheability; } } 

    private HttpCacheability _cacheability;

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
        cache.SetCacheability(_cacheability);
    }
}
Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • 2
    Thanks! That worked, I also found that using jquery.ajax instead of the helper and using `cache: false` solves the problem. But yours will allow me not to have to edit all my views to use jquery. On the other side I would expect the helper to have a cache option just like jquery.ajax does :/ Thanks again! – LanFeusT Jan 13 '12 at 19:08
  • 1
    as another alternative, I like controlling at the controller level: http://stackoverflow.com/questions/1160105/asp-net-mvc-disable-browser-cache/5546328#5546328 – Adam Tuliper Jan 13 '12 at 20:30
  • 1
    @AdamTuliper You can target controllers with this attribute, too. – Craig Stuntz Jan 13 '12 at 20:51
  • @CraigStuntz sure - I just prefer a base controller - either way works just providing something in addition : ) – Adam Tuliper Jan 14 '12 at 07:36
  • 1
    You can certainly target a base controller with this attribute, and that's fine, but you should not use OutputCache when you want the Cache-Control header, because those are two totally different things. – Craig Stuntz Jan 15 '12 at 01:22
  • @CraigStuntz I don't believe that's correct a simple fiddler test shows otherwise . See my post above. – Adam Tuliper Jan 15 '12 at 21:15
  • So you're saying you think this does not affect the output cache? That rather belies the name, no? – Craig Stuntz Jan 16 '12 at 04:37