Archive for April, 2008

April 21st 2008

YSlow and ASP.NET – Expires Header – Expression Builders (Part 2 of 3)

Continuing our task of caching indefinitely static content by versioning it, we want to build the framework so that instead of code like this:

<asp:image imageurl="~/images/rose.jpg" runat="server" id="image1">

we have this:

<asp:Image id="image1" imageurl="<%$ Image: rose.jpg %>" runat="server"/>

The key difference is that we can dynamically assign where the image path for rose.jpg resolves in the latter case. Specifically, we can resolve to applicationPath/versionX.X.X/Images/rose.jpg (or whatever url scheme meets your fancy).

We need two things. First we create an expressionBuilder object and then tie the custom expressionBuilder into the build. I’m using the VS “WebSite” project, but this should work for “Web Application Projects”.

The following code draws heavily from this post on Dave Reed’s excellent blog infinitiesloop.

Imports System.CodeDom
Imports System.Web         

Namespace StaticContent         

	Public NotInheritable Class PathBuilder         

		Private Shared CssPath As String = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/StaticContent/{1}/Css/{{0}}", System.Web.HttpContext.Current.Request.ApplicationPath, System.Configuration.ConfigurationManager.AppSettings("versionNumber")))
		Private Shared ImagePath As String = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/StaticContent/{1}/Images/{{0}}", System.Web.HttpContext.Current.Request.ApplicationPath, System.Configuration.ConfigurationManager.AppSettings("versionNumber")))
		Private Shared JavascriptPath As String = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/StaticContent/{1}/Javascripts/{{0}}", System.Web.HttpContext.Current.Request.ApplicationPath, System.Configuration.ConfigurationManager.AppSettings("versionNumber")))         

		Public Shared Function ConvertCssUrl(ByVal path As String) As String
			Return String.Format(System.Globalization.CultureInfo.InvariantCulture, CssPath, path)
		End Function         

		Public Shared Function ConvertImageUrl(ByVal path As String) As String
			Return String.Format(System.Globalization.CultureInfo.InvariantCulture, ImagePath, path)
		End Function         

		Public Shared Function ConvertJavascriptUrl(ByVal path As String) As String
			Return String.Format(System.Globalization.CultureInfo.InvariantCulture, JavascriptPath, path)
		End Function         

		Private Sub New()
		End Sub         

	End Class         

	MustInherit Class StaticContentExpressionBuilder
		Inherits Compilation.ExpressionBuilder         

		Protected Shared evaluationExpression As String         

		Protected MustOverride ReadOnly Property StaticContentExpression() As String         

		Public Overrides Function ParseExpression(ByVal expression As String, ByVal propertyType As System.Type, ByVal context As Compilation.ExpressionBuilderContext) As Object
			Return String.Format(System.Globalization.CultureInfo.InvariantCulture, StaticContentExpression, expression)
		End Function         

		Public Overloads Overrides Function GetCodeExpression(ByVal entry As System.Web.UI.BoundPropertyEntry, ByVal parsedData As Object, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As System.CodeDom.CodeExpression
			Return New CodeSnippetExpression(parsedData.ToString())
		End Function         

	End Class         

	<Compilation.ExpressionPrefix("Js")> _
 Class JavascriptExpressionBuilder
		Inherits StaticContentExpressionBuilder         

		Protected Overrides ReadOnly Property StaticContentExpression() As String
			Get
				Return "StaticContent.PathBuilder.ConvertJavascriptUrl( ""{0}"" )"
			End Get
		End Property         

	End Class         

	<Compilation.ExpressionPrefix("Image")> _
	Class ImageExpressionBuilder
		Inherits StaticContentExpressionBuilder         

		Protected Overrides ReadOnly Property StaticContentExpression() As String
			Get
				Return "StaticContent.PathBuilder.ConvertImageUrl( ""{0}"" )"
			End Get
		End Property         

	End Class         

	<Compilation.ExpressionPrefix("Css")> _
	Class CssExpressionBuilder
		Inherits StaticContentExpressionBuilder         

		Protected Overrides ReadOnly Property StaticContentExpression() As String
			Get
				Return "StaticContent.PathBuilder.ConvertCssUrl( ""{0}"" )"
			End Get
		End Property         

	End Class         

End Namespace

I’ve chosen to implement several types of static content (css, images, and js) but that’s just a matter of convenience. Clearly this could all be done with a single expression. Note the reason I have placed the content type specific methods on PathBuilder is that I need to call these methods in part three (code-behind) where accessing the expressionBuilder assemblies would be pretty awkward.

To tie our new expression builders into the build we need to add some entries to the web.config.

<compilation debug="true" strict="true" explicit="true">
	<expressionBuilders>
		<add expressionPrefix="Css" type="StaticContent.CssExpressionBuilder"/>
		<add expressionPrefix="Image" type="StaticContent.ImageExpressionBuilder"/>
		<add expressionPrefix="Js" type="StaticContent.JavascriptExpressionBuilder"/>
	</expressionBuilders>
</compilation>

That’s it. Now we can do things in our markup like:

<asp:image imageurl="Image: rose.jpg" runat="server" id="image1">
<link rel="stylesheet" href="Css: Fonts.css %>" type="text/css" id="FontsCss" runat="server">

the image “rose.jpg” and css file “Fonts.css” will be rendered to the page as urls:
“applicationPath/StaticContent/1.0.0/Images/rose.jpg” and “applicationPath/StaticContent/1.0.0/Css/Fonts.css” respectively.

1 Comment »

« Prev - Next »