I have a MSBuild script set up to minify and combine my javascript and css files. What I need now is a way to version them. How are you guys currently handling this. What is the best way to incrementally version the file and update the <script/>
tag with the new file name?
views:
221answers:
1
A:
I was originally going to suggest using resource expressions to include a version tag from AppSettings
, but after testing found that it only works if it is the entire value of a server control property.
The AppSettings
value can be updated by the build script for every release; can be any format you like:
<appSettings>
<add key="versionTag" value="27" />
</appSettings>
Works:
<asp:Label runat="server" Text="<%$ AppSettings: versionTag %>" />
Doesn't work:
<link runat="server" rel="Stylesheet" type="text/css"
href='/css/site.css?v=<%$ AppSettings: versionTag %>' />
So my actual recommendation is to create your own controls that read the version tag and include that in their output. Here's how my CSS control looks on a page (NOTE: server controls must be inside the server-side form, even though it may render inside the head
element):
...
<form id="form1" runat="server">
<my:Stylesheet runat="server" Url="~/css/site.css" />
</form>
...
The code for my Stylesheet control:
namespace MyNamespace
{
using System;
using System.ComponentModel;
using System.Configuration;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
/// <summary>
/// Outputs a CSS stylesheet link that supports versionable caching via a
/// build-specific query parameter.
/// </summary>
[
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.LinkDemand,
Level = AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Href"),
ToolboxData(@"<{0}:Stylesheet runat=""server"" />")
]
public class Stylesheet : WebControl
{
private static string versionTag = Stylesheet.GetVersionTag();
/// <summary>
/// Gets or sets the stylesheet URL.
/// </summary>
public string Href
{
get
{
return this.ViewState["Href"] as string;
}
set
{
this.ViewState["Href"] = value;
}
}
/// <summary>
/// Raises the PreRender event.
/// </summary>
/// <param name="e">Contains the event data.</param>
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
HtmlLink link = new HtmlLink();
link.Href = String.Format(
"{0}?v={1}",
this.Page.ResolveUrl(this.Href),
HttpUtility.UrlEncode(Stylesheet.versionTag));
if (!Stylesheet.HeadContainsLinkHref(this.Page, link.Href))
{
link.Attributes["type"] = "text/css";
link.Attributes["rel"] = "Stylesheet";
this.Page.Header.Controls.Add(link);
}
}
/// <summary>
/// Generates content to be rendered on the client.
/// </summary>
/// <param name="writer">Receives the server control content.</param>
protected override void Render(HtmlTextWriter writer)
{
// Do nothing.
}
/// <summary>
/// Retrieves the script version tag for this build.
/// </summary>
/// <returns>Returns the script version tag.</returns>
private static string GetVersionTag()
{
string tag = ConfigurationManager.AppSettings["versionTag"];
if (String.IsNullOrEmpty(tag))
{
tag = "1";
}
return tag;
}
/// <summary>
/// Determines if the page's <c>head</c> contains a <c>link</c> tag
/// with a matching <c>href</c> attribute value.
/// </summary>
/// <param name="thePage">The Page to be tested.</param>
/// <param name="href">The <c>href</c> URL to be matched.</param>
/// <returns>Returns true if a matching link is already part of the
/// page <c>head</c> or false otherwise.</returns>
public static bool HeadContainsLinkHref(Page thePage, string href)
{
if (thePage == null)
{
throw new ArgumentNullException("thePage");
}
foreach (Control control in thePage.Header.Controls)
{
if ((control is HtmlLink) &&
(control as HtmlLink).Href == href)
{
return true;
}
}
return false;
}
}
}
HTH.
devstuff
2009-10-03 04:37:17