1

I am having problems with partial views being cached that I do not want to be. I have googled this and there are several ways around this, none of which seem to work for me.

Basically I have an action on the server that returns a partial view. I call it firstly from the Html.RenderPartial on page load. And then all the other calls are from jQuery on the same page, when links are clicked, which then refresh the section of the page the partial view is on.Each link that is pressed sends different data, so teh first time each link is clicked it calls teh server, but every subsequent click is rendered from a cached copy.

On my _Layout page (my master page) I have the following at teh top:

<script type="text/javascript">
        $.ajaxSetup({
            cache: false
        });
</script>

And each ajax call has the cache disabled like this:

$.ajax({
        url: UrlToGetEmployeeGrid,
        type: 'GET',
        cache: 'false',
        success: function (response) {
            $EmployeeGridObject.html(response);
        },
        error: function (xhr) {
            alert('There was an error contacting the server to refresh the employee grid');
        }
    });

It is never reaching the server for the calls. But I also tried to add (just in case) the following to the controller action:

[HttpGet]
[OutputCache(Duration = 0)]

But this gives me an error:

System.InvalidOperationException: Duration must be a positive number.

I also tried:

[HttpGet]
[OutputCache(Duration = 0, VaryByParam="none")]

And got the same error.

Any ideas how I can stop caching from ajax calls to an action properly?

UPDATE:

Following on from Paulo's comment, this works fine in Chrome, but doesn't work in IE. I have added a random number to get around this issue for this one particular case. However, long term I need a better solution, as there will be cases where I need to cache actions for a time, that IE would need to be able to do.

eyeballpaul
  • 1,725
  • 2
  • 25
  • 39
  • 1
    This is usually a typical IE problem. [$.getJSON returning cached data in IE8](http://stackoverflow.com/questions/264216/getjson-returning-cached-data-in-ie8). Another simple hack is to add a random parameter in your GET request: [How to stop Internet Explorer from caching AJAX requests](http://www.codecouch.com/2009/01/how-to-stop-internet-explorer-from-caching-ajax-requests/). – Paolo Moretti Dec 13 '11 at 12:00
  • Ah, I have just tested in Chrome and it works perfectly, it was only IE that had the issue. So how on earth would I control caching for an action where I want it to cache, but only by certain parameters, and for a certain time, if IE jsut caches it all the time regardless of my settings? In this instance I couldn't add a random parameter! – eyeballpaul Dec 13 '11 at 12:19
  • In my opinion every AJAX request should be non-cacheable. This is the default behaviour for pages fetched with POST requests. So change your controller method to accept only POST requests using the [`[HttpPost]`](http://msdn.microsoft.com/en-us/library/system.web.mvc.httppostattribute.aspx) attribute and set `type = 'POST'` in your jQuery request. – Paolo Moretti Dec 13 '11 at 13:33
  • The way I want a lot of my application to work, is through ajax requests, rather than full page refreshes. And there will be a lot of data that will be requested through ajax i.e. I will be including charting controls and the like. I was hoping to use the standard cache duration etc of the ajax calls to limit the hit on the database. Also, the ajax calls I am processing are mostly get requests. I only use the post requests when the call is changing something on the server side – eyeballpaul Dec 13 '11 at 14:08

3 Answers3

3

I set the NoStore property and change the VaryByParam to "*".

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
brheal
  • 1,237
  • 8
  • 14
  • 1
    If I add that attribute, then when I call RenderAction I get the following error: "System.InvalidOperationException: Duration must be a positive number." – eyeballpaul Dec 13 '11 at 11:47
  • 1
    A search showed me the problem you are having. I'm running that in a production environment. As soon as I get a chance I'll see how it's working for me. – brheal Dec 13 '11 at 12:37
  • Thanks. If you have a look at Paulo's comment, you will see the initial issue is due to IE, however the issue I am having adding that attribute is still something that is causing me issues. – eyeballpaul Dec 13 '11 at 12:47
3

You can create your own NoCacheAttribute like this:

public class NoCacheAttribute : FilterAttribute, IActionFilter
{
    #region IActionFilter Members
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        return;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();
    }
    #endregion
}

Than you can apply it to your action:

[HttpGet]
[NoCache]

Should solve your problem (I was using this solution for quite long time now).

tpeczek
  • 23,867
  • 3
  • 74
  • 77
  • Thanks for the answer, however, why is this needed? Surely this is what the [OutputCache] attribute is for? – eyeballpaul Dec 13 '11 at 14:40
  • The first reason is what you have already noticed - you can't set duration to 0 if this action will be used as a child action (this is validated inside the OutputCacheAttribute). The second reason is that the OutputCacheAttribute often doesn't take effect when you are returning something that doesn't go through view engine (for example JSON - and IE sometimes likes to cache that as well). – tpeczek Dec 13 '11 at 14:57
1

EDIT: Here is something that will work

Appending a Guid as a random string would certainly get round this apparent IE bug (and be truly random). e.g. append r=Guid.NewGuid().ToString() to the query string or similar.

In your case (using javascript) it's a little more complicated and the complexity for generating UUIDs in javascript is well known, but can be done like this:

See here: https://github.com/broofa/node-uuid and then maybe here: https://gist.github.com/982883 for this gem:

function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

Bottom line: try this one-liner javascript function for generating a random string to append to your query string to prevent caching: http://jsfiddle.net/Dommer/yQWht/

Tom Chantler
  • 14,753
  • 4
  • 48
  • 53
  • You can't add this attribute, as always get the following: "System.InvalidOperationException: Duration must be a positive number." – eyeballpaul Dec 13 '11 at 14:39
  • Oh, sorry. In that case, what about adding the GUID to the query string? It's guaranteed to be unique (and thus not to be cached). – Tom Chantler Dec 13 '11 at 15:24