24
votes

How to do I prevent a Silverlight XAP file being cached by the web browser?

The reason I want to do this is during development I don't want to manually clear the browser cache, I'm looking for a programmatic approach server side.

7

7 Answers

26
votes

Using IIS management add a custom header Cache-Control with the value no-cache. That'll cause the browser to check that any cached version of the XAP is the latest before using it.

8
votes

Add a query parameter to the URL for the XAP in the element on the HTML Page:

  • clientBin/MyApp.xap?rev=1
  • clientBin/MyApp.xap?rev=2

It will be ignored and break the cache. In IE8, there are some cache management tools: Open the Developer tools:

  • Try Cache...Always Refresh from Server
  • Try Cache...Clear Browser Cache for this domain...
6
votes

The solution presented here is somewhat similar to Michael's but is automatic and guarantees the client will always get a new version. This may be inefficient depending on your situation.

Since Lars says in his comments that he is not on Stack Overflow, I'm copying the response here.

<object id="Xaml1" data="data:application/x-silverlight-2,
    "type="application/x-silverlight-2" width="100%" height="100%">

  <%––<param name="source" value="ClientBin/SilverlightApp.xap"/>––%>

  <%     
    string orgSourceValue = @"ClientBin/SilverlightApp.xap";     
    string param;

    if (System.Diagnostics.Debugger.IsAttached)     
    {
        param = "<param name=\"source\" value=\"" + orgSourceValue + "\" />";
    }
    else     
    {     
      string xappath = HttpContext.Current.Server.MapPath(@"") + @"\" + orgSourceValue;

      DateTime xapCreationDate = System.IO.File.GetLastWriteTime(xappath);      

      param = "<param name=\"source\" value=\"" + orgSourceValue + "?ignore=" +
                xapCreationDate.ToString() + "\" />";     
    }

    Response.Write(param);     
  %>

  ....

</object>
5
votes

Create a custom http handler for handling *.xap files and then set your caching options inside the handler.

Something like this...

using System;
using System.IO;
using System.Web;

public class FileCacheHandler : IHttpHandler
{
    public virtual void ProcessRequest(HttpContext context)
    {
        if (File.Exists(context.Request.PhysicalPath))
        {
            DateTime lastWriteTime = File.GetLastWriteTime(filePath);
            DateTime? modifiedSinceHeader = GetModifiedSinceHeader(context.Request);

            if (modifiedSinceHeader == null || lastWriteTime > modifiedSinceHeader)
            {
                context.Response.AddFileDependency(filePath);
                context.Response.Cache.SetLastModifiedFromFileDependencies();
                context.Response.Cache.SetCacheability(HttpCacheability.Public);
                context.Response.TransmitFile(filePath);
                context.Response.StatusCode = 200;
                context.Response.ContentType = "application/x-silverlight-app";
                context.Response.OutputStream.Flush();
            }
            else
            {
                context.Response.StatusCode = 304;
            }
        }
    }

    public DateTime? GetModifiedSinceHeader(HttpRequest request)
    {
        string modifiedSinceHeader = request.Headers["If-Modified-Since"];
        DateTime modifiedSince;
        if (string.IsNullOrEmpty(modifiedSinceHeader)
          || modifiedSinceHeader.Length == 0
          || !DateTime.TryParse(modifiedSinceHeader, out modifiedSince))
            return null;

        return modifiedSince;
    }
}
5
votes

I added a query parm to the path of the xap file, so that I can manage it through Versioning.

Default.aspx code:

<param
   name="source"
   value="ClientBin/MySilverLightApp.xap?xapid<%=XapID %>" />

Default.aspx.cs code:

protected string XapID
{
    get
    {
        Version v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

        if (System.Diagnostics.Debugger.IsAttached)
            Response.Write(string.Format("Build: {0}.{1}.{2}.{3}", v.Major.ToString(), v.Minor.ToString(), v.Build.ToString(), v.Revision.ToString()));
        return string.Format("{0}.{1}.{2}.{3}", v.Major.ToString(), v.Minor.ToString(), v.Build.ToString(), v.Revision.ToString()
    }
}
2
votes

Well all the above examples depend on the browser NOT caching the HTML that contains the new trick xap name.... so you just move the problem on to something else. And they are also fiendishly complicated....

However for the debugging case, at least, it's easy to write the <object> and <param> tags in javascript so that the name changes every time the html page is used, whether it's cached by the browser or not!

<script type="text/javascript">
   document.write('<object blah blah >');
   document.write('<param name="Source" value="myapp.xap?'
               + new Date().getTime()+'">');
   document.write('</object>'); 
</script>

This sidesteps any hassle you may have controlling server settings and works just as well regardless of the server technology in use.

Note: you have to write the whole object group with the same method because putting a script tag inside the object tag means "only do this if the browser doesnt support the object.

2
votes

It’s not very uncommon to run into .XAP caching, which means that every time you deploy a new version of the Silverlight application, the browser does not download the updated .XAP file.

One solution could be to change the IIS properties. You can turn the “Enable Content Expiration HTTP header” option on for your .XAP file by following these step:

  1. Open IIS Manager
  2. Go to “Default Web Site” and find web site for your Silverlight project.
  3. Find the .XAP file under ClientBin.
  4. Go to the properties page of the .XAP file, on HTTP Headers Tab, Turn on “Enable Content Expiration”, click the “Expire Immediately” radio button.
  5. Save the changes.

This way the latest .XAP (only if there is a latest .XAP file) will get downloaded when you refresh your page without having to close the browser.

Hope this helps!