<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Technical Jargon &#187; MonoRail</title>
	<atom:link href="http://www.jeremyskinner.co.uk/category/monorail/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jeremyskinner.co.uk</link>
	<description>Did you notice the information bar?</description>
	<lastBuildDate>Mon, 08 Mar 2010 20:17:49 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Embedded Resources with MonoRail Applications</title>
		<link>http://www.jeremyskinner.co.uk/2007/11/12/embedded-resources-with-monorail-applications/</link>
		<comments>http://www.jeremyskinner.co.uk/2007/11/12/embedded-resources-with-monorail-applications/#comments</comments>
		<pubDate>Mon, 12 Nov 2007 11:36:33 +0000</pubDate>
		<dc:creator>Jeremy Skinner</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MonoRail]]></category>

		<guid isPermaLink="false">http://jeremyskinner.wordpress.com/2007/11/12/embedded-resources-with-monorail-applications/</guid>
		<description><![CDATA[I have a number of JavaScript files that I like to share amongst my web projects. Rather than keeping a separate copy of these script files in each application, I prefer to embed them in to an assembly that the various web projects can reference. But how do you reference these JavaScript files in your [...]]]></description>
			<content:encoded><![CDATA[<p>I have a number of JavaScript files that I like to share amongst my web projects. Rather than keeping a separate copy of these script files in each application, I prefer to embed them in to an assembly that the various web projects can reference. But how do you reference these JavaScript files in your HTML?</p>
<p>Using ASP.NET WebForms this is easy &#8211; you simply add a [assembly: WebResource] attribute to your AssemblyInfo.cs file and then you can call Page.ClientScript.GetWebResourceUrl to generate a URL to an HTTP Handler that will extract the resource from the assembly and output its contents (see <a href="http://aspnet.4guysfromrolla.com/articles/080906-1.aspx">this article</a> on 4guys for more information)</p>
<p>I wanted to do a similar thing using <a href="http://www.castleproject.org">MonoRail</a>, so I wrote a Controller and a Helper that perform a similar task.</p>
<p>The first thing I do is create a helper that will look up the names of all the embedded resources in the assembly. To do this, I use a static constructor to ensure that this only happens the first time that the class is used.</p>
<pre>
public class ScriptHelper : AbstractHelper
{
    internal const string ROOT_NAMESPACE = "Cristal.Common.Resources";
    private static readonly List&lt;string&gt; _resources = new List&lt;string&gt;();
    private static readonly string _version;

    static ScriptHelper()
    {
        _version = Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace(".", "");
        foreach (string resource in typeof(ScriptHelper).Assembly.GetManifestResourceNames())
        {
            _resources.Add(resource);
        }
    }
}</pre>
<p>The static constructor finds all of the assembly-level resources and adds their names to a cache. I also look up the version of the currently executing assembly &#8211; more on this later. The constant ROOT_NAMESPACE variable simply stores the namespace where the embedded resources are stored.</p>
<p>Finally, I have a public method called &#8220;Install&#8221; that can be called from my Views to reference one of these embedded scripts:</p>
<pre>
public string Install(string name)
{
    NameValueCollection querystring = new NameValueCollection(2);
    querystring.Add("n", name); //name of script
    querystring.Add("v", _version); //version
    string url = Controller.UrlBuilder.BuildUrl(Controller.Context.UrlInfo,
                                                string.Empty,
                                                "Resources",
                                                "Scripts",
                                                querystring);

    //cut off the final ampersand which monorail irritatingly appends.
    url = url.Substring(0, url.Length - 1);
    url = HttpUtility.HtmlEncode(url);
    return string.Format("&lt;script type=\"text/javascript\" src=\"{0}\"&gt;&lt;/script&gt;", url);
}</pre>
<p>This method generates a URL to a pretend controller with the name of the script (and the assembly version) as querystring parameters. I can then declare this helper on my controller, and reference it in the view:</p>
<p>Controller:</p>
<pre>
[Helper(typeof(ScriptHelper), "Script")]
public class MyController : Controller
{
	public void Index() {}
}</pre>
<p>And the view:</p>
<pre>
${Script.Install("SomeScriptName")}</pre>
<p>Note that the name of the script is not the full web resource name (eg Cristal.Common.Resources.SomeScriptName.js)</p>
<p>In the HTML, this will generate some thing like:</p>
<pre>
&lt;script type="text/javascript" src="/Resources/Scripts.rails?n=SomeScriptName&amp;v=1000"&gt;&lt;/script&gt;</pre>
<p>The &#8220;n&#8221; querystring parameter is the name of the script and the &#8220;v&#8221; is the assembly version.<br />
Of course, this won&#8217;t actually work at the moment as there isn&#8217;t a Resources controller to handle the request. So let&#8217;s create one:</p>
<pre>
public class ResourcesController : Controller
{

    //n stands for "name", v stands for "version"
    [Cache(HttpCacheability.Public, Duration = 86400, VaryByParams = "v,n")]
    public void Scripts()
    {
    	if (Request.QueryString["n"] == null)
            throw new Exception("Invalid resource.");
	    Response.ContentType = "text/javascript";
        RenderText(ScriptHelper.GetResource(HttpUtility.HtmlEncode(Request.QueryString["n"])));
    }
}</pre>
<p>The &#8220;Scripts&#8221; action is very simple &#8211; it ensures that a script name is present in the query string, then passes it to the GetResource method on our scripthelper (below). Also notice the [Cache] attribute at the top of the action &#8211; once the script has been loaded from the assembly, the response is cached. However, to ensure that each script is cached separately, we specify the &#8220;n&#8221; query string parameter (the script name) in the VaryByParams option.</p>
<p>&#8220;v&#8221; is also in VaryByParams so that I can force the cache to expire by incrementing the version number.</p>
<p>Here&#8217;s the GetResource method on ScriptHelper:</p>
<pre>
internal static string GetResource(string name)
{
    name = BuildResourceName(name);

    string toReturn = string.Empty;
    if (name != null)
    {
     	Assembly asm = typeof(ScriptHelper).Assembly;
        using (StreamReader reader = new StreamReader(asm.GetManifestResourceStream(name)))
        {
        	toReturn = reader.ReadToEnd();
        }
    }
    return toReturn;
}

private static string BuildResourceName(string name)
{
    name = string.Format("{0}.{1}.js", ROOT_NAMESPACE, name);
    bool hasResource = false;
    foreach (string resource in _resources)
    {
    	if (name.Equals(resource, StringComparison.InvariantCultureIgnoreCase))
        {
            hasResource = true;
            name = resource;
            break;
        }
    }
    if (!hasResource)
    	return null;

    return name;
}</pre>
<p>Firstly, BuildResourceName is called to convert the script name (eg &#8220;SomeScriptName&#8221;) in to the fully qualified Web-resource name (eg &#8220;Cristal.Common.Resources.SomeScriptName.js&#8221;). Then, we use a streamreader to extract the contents of the JavaScript file from the embedded resource, convert it to a string and return it to the controller.</p>
<p>I&#8217;ve implemented this in the next version of CristalWeb (version 7).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeremyskinner.co.uk/2007/11/12/embedded-resources-with-monorail-applications/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
