8

Possible Duplicate:
CSS in App_Theme folder gets Cached in Browser

I've seen "What is an elegant way to force browsers to reload cached CSS/JS files?" but the answer there uses PHP and it doesn't address the fact that the CSS is injected dynamically by an ASP.Net Theme.

Community
  • 1
  • 1
Homer
  • 7,594
  • 14
  • 69
  • 109
  • Hi Homer, I dont know whether this will work or not as its just a theory. Im guessing browsers cache the css file and get a new version if they can see it is changed. My idea would be to have a class in the css file that is not used and different for each theme. If this class is at the top of each css file then the browser would read it, see the different class name and redownload the css file. This is of course all based on assumptions and no knowledge on how internet explorer caches css. I would be interested also if anyone else can speak with authority on this! – WraithNath Jan 19 '11 at 15:17
  • I'm a bit confused- When you use a different theme the stylesheet is located in another folder ie /App_Themes/blue/stylesheet.css vs /App_Themes/red/stylesheet.css. That directory change is enough to cause it to not be cached. Now, of course red/stylesheet.css will still be cached, but if you swap themes to blue, that will load the blue one (which might be cached). – Prescott Jan 19 '11 at 21:11
  • I'm not swapping Themes, just modifying the css file. – Homer Jan 19 '11 at 21:56

4 Answers4

14

I think I have a quick and dirty solution. The trick is to examine the controls within the page header (for example in the PreRender phase), find the links pointing to CSS-files under the App_Themes folder and make them dynamic (by adding some random information to the query-string). This will most likely tell the browser to invalidate the cached version of the file.

The code:

protected void Page_PreRender(object sender, EventArgs e)
{
    HtmlLink link = null;

    foreach (Control c in Header.Controls)
    {
        if (c is HtmlLink)
        {
            link = c as HtmlLink;

            if (link.Href.IndexOf("App_Themes/", StringComparison.InvariantCultureIgnoreCase) >= 0 &&
                link.Href.EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
            {
                link.Href += string.Format("?t={0}", DateTime.Now.Ticks.ToString());
            }
        }
    }
}

The output:

    <link href="App_Themes/MyTheme/MyTheme.css?t=634310637798128189" 
        type="text/css" rel="stylesheet" />

Note that you need to have a <head runat="server"> declared in your page's markup in order to be able to access the Header property (otherwise it will be null).

volpav
  • 5,090
  • 19
  • 27
  • This method would definitely kill css browser caching since the filename would be different every time a user viewed the page. Anyway, I've read that browsers will not cache files with a querystring param. – Homer Jan 19 '11 at 20:51
  • @Homer: They might cache such files, it depends on the implementation. Why not cache the file if its URI (not the URL) never changes? – volpav Jan 20 '11 at 06:38
  • @Homer, this basic idea worked for me, I had to change a little however. Here is my change: protected override void OnPreRender(object sender, EventArgs e) also, you need to make sure you call the base method before the method is finished. – Jonathan Henson May 31 '11 at 03:27
  • 4
    You could do it by generating the Ticks from the lastmodified date on the file - would be slower of course, but would properly reflect the state of the file w.r.t. cache. – Joe Niland Feb 27 '12 at 03:27
  • Beware of the DateTime.Now.Ticks. You permanently disable all caching; this *will* make your site a lot slower. Use the file date/time, possibly with extra logic to stop retrieving it too often. – Roman Starkov Feb 21 '14 at 20:38
1

I've not found a way to version App_Themes files (yet), but you can set them to expire in a relatively short period of time (e.g., 10 seconds), which will minimize the cache problem while giving you some performance benefit of caching. Remember, if the file has not changed, the server will respond with 304-Not modified so traffic is reduced even if the browser requests the file.

Add this to the <configuration> section of the Web.Config

<location path="App_Themes">
    <system.webServer>
        <staticContent>
            <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:00:10" />
        </staticContent>
    </system.webServer>
</location>
Doug Domeny
  • 4,410
  • 2
  • 33
  • 49
1

If you are asking how the SERVER side can force reloading... One way is to dynamically change the filename of the CSS/JS so that subsequent calls to the page require a different file.

<sarcasm> The other is to simply tell the user to press CTRL-F5 :) </sarcasm>

Dekker500
  • 821
  • 5
  • 7
  • Any idea how to do that in ASP.Net (dynamically change the filename, not CTRL-F5)? – Homer Mar 09 '11 at 19:16
  • 2
    One method I've used is to have rewrite rules on the server, redirecting any request for a file of a particular pattern (/css/mycssfile.201104056554433.css) to the real file which does not have a timestamp. This way the browser treats each request as a request for a unique file, and your server can just blindly return a request for the file, regardless of the actual timestamp. You just need to remember in your asp page to dynamically change the css filename being requested on each request (easy, just insert the current timestamp on each request). – Dekker500 Mar 15 '11 at 13:58
1

When finished doing changes to the site change the name of the css manually.

Esger
  • 1,310
  • 12
  • 18