<?xml version="1.0" encoding="iso-8859-1"?><!-- generator="b2evolution/4.0.3" -->
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Web Developer - Author(s): Alex Ullrich</title>
		<link>http://blogs.lessthandot.com/index.php/WebDev/</link>
		<atom:link rel="self" type="application/rss+xml" href="http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2" />
		<description></description>
		<language>en-GB</language>
		<docs>http://blogs.law.harvard.edu/tech/rss</docs>
		<admin:generatorAgent rdf:resource="http://b2evolution.net/?v=4.0.3"/>
		<ttl>60</ttl>
				<item>
			<title>SquishIt and Nancy</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/squishit-and-nancy</link>
			<pubDate>Fri, 11 Jan 2013 13:11:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">ASP.NET</category>
<category domain="alt">AJAX</category>			<guid isPermaLink="false">2019@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Everybody&#039;s favorite &lt;a href=&quot;http://blogs.lessthandot.com/index.php/All/?disp=authdir&amp;amp;author=7&quot;&gt;LTD blogger&lt;/a&gt; / &lt;a href=&quot;http://twitter.com/chrissie1&quot;&gt;Belgian tweeter&lt;/a&gt; Chris asked me last week how he could get SquishIt working with the &lt;a href=&quot;http://nancyfx.org/&quot;&gt;Nancy web framework&lt;/a&gt;.  I had to admit, I had no idea.  But couldn&#039;t imagine it would be that much more difficult than making it work with ASP.net MVC.  So I decided to look into it.  It turned out to be pretty much the same, with one extra step.  I started out by installing the packages I needed from NuGet to an empty asp.net application:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb75963&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;gt; install-package SquishIt&lt;br /&gt;&amp;gt; install-package Nancy&lt;br /&gt;&amp;gt; install-package Nancy.Hosting.AspNet&lt;br /&gt;&amp;gt; install-package Nancy.Viewengines.Razor&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb97926&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once &lt;/p&gt;
&lt;h3&gt;Configuring Nancy&#039;s View Engine&lt;/h3&gt;

&lt;p&gt;This was infinitely more complex than using a referenced library in a razor view with MVC.  Translation: this was as simple as adding a &quot;razor&quot; section to the web.config:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb41361&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;configSections&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;razor&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Nancy.ViewEngines.Razor.RazorConfigurationSection, Nancy.ViewEngines.Razor&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/configSections&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;razor&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;disableAutoIncludeModelNamespace&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;assemblies&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;assembly&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;assembly&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;SquishIt.Framework&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/assemblies&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;namespaces&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;namespace&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;SquishIt.Framework&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/namespaces&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/razor&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb94620&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Its worth noting that when I added SquishIt.Framework to the namespaces section it &lt;strong&gt;worked&lt;/strong&gt;, but didn&#039;t help with intellisense.  So I ended up adding the @using directives in my views anyway.  So if you want intellisense, don&#039;t bother with the namespaces if redundancy bothers you.&lt;/p&gt;

&lt;h3&gt;Static Bundles&lt;/h3&gt;

&lt;p&gt;The typical SquishIt use involves writing a new css or javascript file to the server&#039;s file system.  At least I think it does - this is certainly what I would consider typical.  So I looked at that first.  First thing I needed to do was figure out how to get Nancy to render a view for me.  It wasn&#039;t terribly difficult, just had to set up a module with the routes involved: &lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb1859&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; Nancy;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;NancySample&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Modules&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; HomeModule : NancyModule&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; HomeModule&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Get&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; = parameters =&amp;gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Hello.cshtml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb91868&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By convention, Nancy locates the view in Views/Home.  I assume it would look in Views/Shared next, but didn&#039;t bother to confirm.  So I added couple javascript files in Content/js, and then added a view with a bundle:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;razor&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;razor&quot; id=&quot;cb3138&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;@using SquishIt.Framework&lt;br /&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;meta charset=&amp;quot;utf-8&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;title&amp;gt;Hello World Page&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;body&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;Hello World Page&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;p&amp;gt;This page will include a javascript bundle that is rendered to the file system and served as a static asset.&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; @Html.Raw(Bundle.JavaScript()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .Add(&amp;quot;~/Content/js/js1.js&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .Add(&amp;quot;~/Content/js/js2.js&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .Render(&amp;quot;~/Content/combined/bundle.js&amp;quot;))&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb79125&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As long as I disabled debugging, a single tag was rendered into my page for bundle.js.  That was easy.&lt;/p&gt;

&lt;h3&gt;Cached Bundles&lt;/h3&gt;

&lt;p&gt;SquishIt also has the ability to render bundles to an internal cache instead of the file system.  This is useful for shared hosting environments.  You can read the initial documentation &lt;a href=&quot;https://github.com/jetheredge/SquishIt/wiki/Using-SquishIt-programmatically-without-the-file-system&quot;&gt;here&lt;/a&gt;.  Getting this to work with Nancy was not really that different - we just needed to create a module to handle serving the assets:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb17554&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IO&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; Nancy;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;Framework&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;NancySample&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Modules&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; AssetsModule : NancyModule&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; AssetsModule&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : &lt;span style=&quot;color: #0600FF;&quot;&gt;base&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/assets&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Get&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/js/{name}&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; = parameters =&amp;gt; CreateResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;JavaScript&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;RenderCached&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;parameters.&lt;span style=&quot;color: #0000FF;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, Configuration.&lt;span style=&quot;color: #0000FF;&quot;&gt;Instance&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;JavascriptMimeType&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Get&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/css/{name}&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; = parameters =&amp;gt; CreateResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;Css&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;RenderCached&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;parameters.&lt;span style=&quot;color: #0000FF;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, Configuration.&lt;span style=&quot;color: #0000FF;&quot;&gt;Instance&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssMimeType&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Response CreateResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; contentType&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; Response&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;FromStream&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; =&amp;gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; MemoryStream&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Encoding.&lt;span style=&quot;color: #0000FF;&quot;&gt;UTF8&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetBytes&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, contentType&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithHeader&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Cache-Control&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;max-age=45&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb10394&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This module renders a cached bundle by name using SquishIt&#039;s globally configured MIME types to render the content.  It also sets a cache-control header on the response, just because I wanted to see how to set headers with Nancy.&lt;/p&gt;

&lt;p&gt;We then need to add a Global.asax and build a bundle:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb70531&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Application_Start&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;object&lt;/span&gt; sender, EventArgs e&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;JavaScript&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;~/Content/js/js1.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;~/Content/js/js2.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;AsCached&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;~/assets/js/hello&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb65717&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second parameter here is called filePath, but actually represents the path to the assets controller, including the &quot;name&quot; parameter.  This is what gets used in the src attribute of the rendered tag.&lt;/p&gt;

&lt;p&gt;Finally we can add a view.  Note that the cached bundle is rendered by name into the page:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;razor&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;razor&quot; id=&quot;cb99378&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;@using SquishIt.Framework&lt;br /&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;meta charset=&amp;quot;utf-8&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;title&amp;gt;Hello World Page&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;body&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;Hello World Page&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;p&amp;gt;This page will include a javascript bundle that is rendered into memory in Global.asax and served through the Assets Module&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; @Html.Raw(Bundle.JavaScript()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .RenderCachedAssetTag(&amp;quot;hello&amp;quot;))&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb4298&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and change our HomeModule to serve the route:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb27737&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; Nancy;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;NancySample&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Modules&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; HomeModule : NancyModule&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; HomeModule&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Get&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; = parameters =&amp;gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Hello.cshtml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Get&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/cached&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; = parameters =&amp;gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;HelloCached.cshtml&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb14733&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Piece of cake.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This was my first exposure to Nancy, and I came away pretty impressed.  Its no-nonsense approach reminds me of other projects I&#039;ve messed around with in the past like &lt;a href=&quot;http://manosdemono.org/&quot;&gt;manos&lt;/a&gt; and &lt;a href=&quot;http://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt;.  I hope to get a chance to play around with it at least a little bit more.&lt;/p&gt;

&lt;p&gt;I&#039;ll think about putting together a package to help with integration (similar to &lt;a href=&quot;http://nuget.org/packages/SquishIt.Mvc/&quot;&gt;SquishIt.Mvc&lt;/a&gt; but its so easy to get going that I&#039;m not sure its needed (that package is only a controller and a few extension methods that return MvcHtmlStrings instead of strings).  I guess I will have to see if there is any demand, or if there are any issues preventing Nancy from being used in shared hosting environments to see if it&#039;d be worth it.&lt;/p&gt;

&lt;p&gt;The sample project can be downloaded in its&#039; entirety at &lt;a href=&quot;https://github.com/AlexCuse/SquishIt.NancySample&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/squishit-and-nancy&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Everybody's favorite <a href="http://blogs.lessthandot.com/index.php/All/?disp=authdir&amp;author=7">LTD blogger</a> / <a href="http://twitter.com/chrissie1">Belgian tweeter</a> Chris asked me last week how he could get SquishIt working with the <a href="http://nancyfx.org/">Nancy web framework</a>.  I had to admit, I had no idea.  But couldn't imagine it would be that much more difficult than making it work with ASP.net MVC.  So I decided to look into it.  It turned out to be pretty much the same, with one extra step.  I started out by installing the packages I needed from NuGet to an empty asp.net application:</p>

<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb25822'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb25822','cb71878'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb25822" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&gt; install-package SquishIt</li><li style="" class="li2">&gt; install-package Nancy</li><li style="" class="li1">&gt; install-package Nancy.Hosting.AspNet</li><li style="" class="li2">&gt; install-package Nancy.Viewengines.Razor</li></ol></div><div id="cb71878" style="display: none; color: red;"></div></div></div>

<p>Once </p>
<h3>Configuring Nancy's View Engine</h3>

<p>This was infinitely more complex than using a referenced library in a razor view with MVC.  Translation: this was as simple as adding a "razor" section to the web.config:</p>

<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb87868'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb87868','cb2737'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb87868" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;configSections<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;section</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;razor&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;Nancy.ViewEngines.Razor.RazorConfigurationSection, Nancy.ViewEngines.Razor&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/configSections<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;razor</span> <span style="color: #000066;">disableAutoIncludeModelNamespace</span>=<span style="color: #ff0000;">&quot;false&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;assemblies<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;add</span> <span style="color: #000066;">assembly</span>=<span style="color: #ff0000;">&quot;System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;add</span> <span style="color: #000066;">assembly</span>=<span style="color: #ff0000;">&quot;SquishIt.Framework&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/assemblies<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;namespaces<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;add</span> <span style="color: #000066;">namespace</span>=<span style="color: #ff0000;">&quot;SquishIt.Framework&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/namespaces<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/razor<span style="font-weight: bold; color: black;">&gt;</span></span></span></li></ol></div><div id="cb2737" style="display: none; color: red;"></div></div></div>

<p>Its worth noting that when I added SquishIt.Framework to the namespaces section it <strong>worked</strong>, but didn't help with intellisense.  So I ended up adding the @using directives in my views anyway.  So if you want intellisense, don't bother with the namespaces if redundancy bothers you.</p>

<h3>Static Bundles</h3>

<p>The typical SquishIt use involves writing a new css or javascript file to the server's file system.  At least I think it does - this is certainly what I would consider typical.  So I looked at that first.  First thing I needed to do was figure out how to get Nancy to render a view for me.  It wasn't terribly difficult, just had to set up a module with the routes involved: </p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb87253'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb87253','cb20011'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb87253" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">using</span> Nancy;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">namespace</span> SquishIt.<span style="color: #0000FF;">NancySample</span>.<span style="color: #0000FF;">Modules</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> HomeModule : NancyModule</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> HomeModule<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Get<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;/&quot;</span><span style="color: #000000;">&#93;</span> = parameters =&gt; View<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Hello.cshtml&quot;</span><span style="color: #000000;">&#93;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb20011" style="display: none; color: red;"></div></div></div>

<p>By convention, Nancy locates the view in Views/Home.  I assume it would look in Views/Shared next, but didn't bother to confirm.  So I added couple javascript files in Content/js, and then added a view with a bundle:</p>

<div class="codebox"><div class="codeheader"><span>razor</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb80747'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb80747','cb46163'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="razor" id="cb80747" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">@using SquishIt.Framework</li><li style="" class="li2">&lt;!DOCTYPE html&gt;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&lt;html lang=&quot;en&quot;&gt;</li><li style="" class="li1">&nbsp; &nbsp; &lt;head&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;meta charset=&quot;utf-8&quot; /&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;title&gt;Hello World Page&lt;/title&gt;</li><li style="" class="li2">&nbsp; &nbsp; &lt;/head&gt;</li><li style="" class="li1">&nbsp; &nbsp; &lt;body&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;h1&gt;Hello World Page&lt;/h1&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;p&gt;Hello World!&lt;/p&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;p&gt;This page will include a javascript bundle that is rendered to the file system and served as a static asset.&lt;/p&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; @Html.Raw(Bundle.JavaScript()</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Add(&quot;~/Content/js/js1.js&quot;)</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Add(&quot;~/Content/js/js2.js&quot;)</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Render(&quot;~/Content/combined/bundle.js&quot;))</li><li style="" class="li1">&nbsp; &nbsp; &lt;/body&gt;</li><li style="" class="li2">&lt;/html&gt;</li></ol></div><div id="cb46163" style="display: none; color: red;"></div></div></div>

<p>As long as I disabled debugging, a single tag was rendered into my page for bundle.js.  That was easy.</p>

<h3>Cached Bundles</h3>

<p>SquishIt also has the ability to render bundles to an internal cache instead of the file system.  This is useful for shared hosting environments.  You can read the initial documentation <a href="https://github.com/jetheredge/SquishIt/wiki/Using-SquishIt-programmatically-without-the-file-system">here</a>.  Getting this to work with Nancy was not really that different - we just needed to create a module to handle serving the assets:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb16402'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb16402','cb61729'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb16402" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">using</span> <span style="color: #000000;">System</span>.<span style="color: #0000FF;">IO</span>;</li><li style="" class="li2"><span style="color: #0600FF;">using</span> <span style="color: #000000;">System</span>.<span style="color: #0000FF;">Text</span>;</li><li style="" class="li1"><span style="color: #0600FF;">using</span> Nancy;</li><li style="" class="li2"><span style="color: #0600FF;">using</span> SquishIt.<span style="color: #0000FF;">Framework</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #0600FF;">namespace</span> SquishIt.<span style="color: #0000FF;">NancySample</span>.<span style="color: #0000FF;">Modules</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> AssetsModule : NancyModule</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> AssetsModule<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : <span style="color: #0600FF;">base</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;/assets&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Get<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;/js/{name}&quot;</span><span style="color: #000000;">&#93;</span> = parameters =&gt; CreateResponse<span style="color: #000000;">&#40;</span>Bundle.<span style="color: #0000FF;">JavaScript</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">RenderCached</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span><span style="color: #000000;">&#41;</span>parameters.<span style="color: #0000FF;">name</span><span style="color: #000000;">&#41;</span>, Configuration.<span style="color: #0000FF;">Instance</span>.<span style="color: #0000FF;">JavascriptMimeType</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Get<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;/css/{name}&quot;</span><span style="color: #000000;">&#93;</span> = parameters =&gt; CreateResponse<span style="color: #000000;">&#40;</span>Bundle.<span style="color: #0000FF;">Css</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">RenderCached</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span><span style="color: #000000;">&#41;</span>parameters.<span style="color: #0000FF;">name</span><span style="color: #000000;">&#41;</span>, Configuration.<span style="color: #0000FF;">Instance</span>.<span style="color: #0000FF;">CssMimeType</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Response CreateResponse<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> content, <span style="color: #FF0000;">string</span> contentType<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> Response</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">FromStream</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> =&gt; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> MemoryStream<span style="color: #000000;">&#40;</span>Encoding.<span style="color: #0000FF;">UTF8</span>.<span style="color: #0000FF;">GetBytes</span><span style="color: #000000;">&#40;</span>content<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>, contentType<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithHeader</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Cache-Control&quot;</span>, <span style="color: #808080;">&quot;max-age=45&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb61729" style="display: none; color: red;"></div></div></div>

<p>This module renders a cached bundle by name using SquishIt's globally configured MIME types to render the content.  It also sets a cache-control header on the response, just because I wanted to see how to set headers with Nancy.</p>

<p>We then need to add a Global.asax and build a bundle:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb69674'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb69674','cb42179'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb69674" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">void</span> Application_Start<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">object</span> sender, EventArgs e<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; Bundle.<span style="color: #0000FF;">JavaScript</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;~/Content/js/js1.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;~/Content/js/js2.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">AsCached</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;hello&quot;</span>, <span style="color: #808080;">&quot;~/assets/js/hello&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb42179" style="display: none; color: red;"></div></div></div>

<p>The second parameter here is called filePath, but actually represents the path to the assets controller, including the "name" parameter.  This is what gets used in the src attribute of the rendered tag.</p>

<p>Finally we can add a view.  Note that the cached bundle is rendered by name into the page:</p>
<div class="codebox"><div class="codeheader"><span>razor</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb62561'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb62561','cb21504'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="razor" id="cb62561" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">@using SquishIt.Framework</li><li style="" class="li2">&lt;!DOCTYPE html&gt;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&lt;html lang=&quot;en&quot;&gt;</li><li style="" class="li1">&nbsp; &nbsp; &lt;head&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;meta charset=&quot;utf-8&quot; /&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;title&gt;Hello World Page&lt;/title&gt;</li><li style="" class="li2">&nbsp; &nbsp; &lt;/head&gt;</li><li style="" class="li1">&nbsp; &nbsp; &lt;body&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;h1&gt;Hello World Page&lt;/h1&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;p&gt;Hello World!&lt;/p&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;p&gt;This page will include a javascript bundle that is rendered into memory in Global.asax and served through the Assets Module&lt;/p&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; @Html.Raw(Bundle.JavaScript()</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .RenderCachedAssetTag(&quot;hello&quot;))</li><li style="" class="li1">&nbsp; &nbsp; &lt;/body&gt;</li><li style="" class="li2">&lt;/html&gt;</li></ol></div><div id="cb21504" style="display: none; color: red;"></div></div></div>

<p>and change our HomeModule to serve the route:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb83112'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb83112','cb23985'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb83112" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">using</span> Nancy;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">namespace</span> SquishIt.<span style="color: #0000FF;">NancySample</span>.<span style="color: #0000FF;">Modules</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> HomeModule : NancyModule</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> HomeModule<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Get<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;/&quot;</span><span style="color: #000000;">&#93;</span> = parameters =&gt; View<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Hello.cshtml&quot;</span><span style="color: #000000;">&#93;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Get<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;/cached&quot;</span><span style="color: #000000;">&#93;</span> = parameters =&gt; View<span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;HelloCached.cshtml&quot;</span><span style="color: #000000;">&#93;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb23985" style="display: none; color: red;"></div></div></div>

<p>Piece of cake.</p>

<h3>Conclusion</h3>

<p>This was my first exposure to Nancy, and I came away pretty impressed.  Its no-nonsense approach reminds me of other projects I've messed around with in the past like <a href="http://manosdemono.org/">manos</a> and <a href="http://servicestack.net/">ServiceStack</a>.  I hope to get a chance to play around with it at least a little bit more.</p>

<p>I'll think about putting together a package to help with integration (similar to <a href="http://nuget.org/packages/SquishIt.Mvc/">SquishIt.Mvc</a> but its so easy to get going that I'm not sure its needed (that package is only a controller and a few extension methods that return MvcHtmlStrings instead of strings).  I guess I will have to see if there is any demand, or if there are any issues preventing Nancy from being used in shared hosting environments to see if it'd be worth it.</p>

<p>The sample project can be downloaded in its' entirety at <a href="https://github.com/AlexCuse/SquishIt.NancySample">github</a>.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/squishit-and-nancy">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/squishit-and-nancy#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=2019</wfw:commentRss>
		</item>
				<item>
			<title>Preprocessor Extensibility in SquishIt 0.9</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/preprocessor-extensibility-in-squishit-0-9</link>
			<pubDate>Fri, 05 Oct 2012 12:38:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>
<category domain="alt">Javascript</category>
<category domain="alt">AJAX</category>
<category domain="alt">XHTML &amp; CSS</category>			<guid isPermaLink="false">1771@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;For the past couple years, .net developers have been embracing various content preprocessors as they become more accessible.  For the same couple of years, we&#039;ve been trying to keep up.  The &lt;a href=&quot;http://www.dotlesscss.org/&quot;&gt;dotLess&lt;/a&gt; port of the popular .less CSS extension has been getting better by leaps and bounds. It has become almost trivial to embed a javascript compiler in .net these days (thanks to projects like &lt;a href=&quot;http://jurassic.codeplex.com/&quot;&gt;Jurassic&lt;/a&gt;), enabling us to support things like coffeescript.  So we&#039;re doing the obvious thing - stripping preprocessor support from our core library.&lt;/p&gt;

&lt;p&gt;There are some good reasons for this.  Why force people to download things like Jurassic or dotLess if they don&#039;t have the need?  The flipside of this is that we&#039;d been deliberately avoiding adding support for SASS/SCSS because of concerns about linking to IronRuby - these concerns largely disappear when preprocessing becomes an opt-in behavior.  Some of these libraries don&#039;t even work on Mono (I think .less might be the only one that works currently) so I feel extra bitter downloading code that won&#039;t run on my platform of choice.  Finally, the growth in adoption has been so fast that frankly, we&#039;re unable to keep up.&lt;/p&gt;

&lt;p&gt;So let&#039;s take a look at some of the original code (well not original as some of our refactorings did find their way to the 0.8.x branch).&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb38047&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;internal&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; PreprocessForDebugging&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; filename&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;filename.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToLower&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;EndsWith&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.coffee&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; js = ProcessCoffee&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;filename&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; filename += debugExtension;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;var fileWriter = fileWriterFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetFileWriter&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;filename&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fileWriter.&lt;span style=&quot;color: #0000FF;&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;js&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; filename;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb33649&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the trigger for preprocessing is the extension.  This is the desired behavior, but the way it was coded left it very brittle and made adding new preprocessors unwieldy.  So we set out to find a way to break this code out of the core library.  &lt;/p&gt;

&lt;p&gt;The approach that we used was plugin based - we defined an interface and exposed a mechanism to register implementations of this interface with the core library.  Our original interface actually checked a file name to see if it needed preprocessing, so you could define any logic you wanted to determine whether to preprocess - we ended up eschewing this to go back to the extension-based decisions, for reasons that will be discussed later.  The interface looks like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb37296&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;interface&lt;/span&gt; IPreprocessor&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; ValidFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; extension&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; IProcessResult Process&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; filePath, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; Extensions &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb52482&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &quot;ValidFor&quot; method does exactly what it says - check if the preprocessor should be used with the supplied extension.  &quot;Process&quot; is where the actual preprocessing happens.  The array of extensions is exposed publicly to be used in registering the preprocessor - this is because each type of content bundle has a list of allowed extensions that is used to filter what gets included when we add a directory full of files.  Finally, the ProcessResult type includes a string representing preprocessed content and a list of any dependent files that were changed.  This last part was added by &lt;a href=&quot;http://twitter.com/SimonPStevens&quot;&gt;Simon Stevens&lt;/a&gt; to enable &lt;a href=&quot;https://github.com/jetheredge/SquishIt/pull/211&quot;&gt;inclusion of .less imports as dependent files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Preprocessors can be registered two ways - both statically and with a particular bundle instance.  For the instance level configuration there is a method in the bundle&#039;s fluent API called &quot;WithPreprocessor&quot; that allows inclusion of a preprocessor with that bundle instance.  Globally, we used the static &quot;Bundle&quot; class to allow preprocessor registration - methods exist there for registering script, style, and global preprocessors.  If preprocessors of the same type are registered both statically and with a bundle instance, the instance-level preprocessor will be used.&lt;/p&gt;

&lt;p&gt;Now, back to why we decided to make preprocessor selection based on extension rather than the complete file name.  To understand, I guess all you have to do is read about the &lt;a href=&quot;http://guides.rubyonrails.org/asset_pipeline.html&quot;&gt;Asset Pipeline&lt;/a&gt; in Ruby on Rails, but I will attempt to summarize here.  The beautiful thing about the pipeline approach is the ability to chain preprocessing steps.  This allows you to use ERB&#039;s helper methods in your file &lt;strong&gt;prior to&lt;/strong&gt; other preprocessing.  For example, if you wanted to use ERB helpers in a coffeescript file you can name your file file.js.coffee.erb - when an asset has the .coffee and .erb extensions, both preprocessors will be applied.  The order they are applied is driven by the reverse order of extensions, so *.coffee.erb would be preprocessed first by ERB and then by the coffeescript compiler.  Our goal was to emulate this behavior in SquishIt, and without matching preprocessors to extensions rather than filenames we wouldn&#039;t have been able to.&lt;/p&gt;

&lt;p&gt;Enabling this behavior is mostly a matter of finding preprocessors correctly.  We find them like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb46897&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; IPreprocessor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; FindPreprocessors&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; file.&lt;span style=&quot;color: #0000FF;&quot;&gt;Split&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&#039;.&#039;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Skip&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Reverse&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Select&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FindPreprocessor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Where&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;p =&amp;gt; p != &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;ToArray&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb38466&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It&#039;s important to note here that &quot;FindPreprocessor&quot; uses the firstpreprocessor it finds for a given extension - so we need to take care if implementing preprocessors for common file extensions like &quot;.js&quot;.  We can then use the preprocessors in the default order to process our content:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb49062&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; PreprocessFile&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; file, IPreprocessor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; preprocessors&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; directoryWrapper.&lt;span style=&quot;color: #0000FF;&quot;&gt;ExecuteInDirectory&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Path.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; =&amp;gt; PreprocessContent&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;file, preprocessors, ReadFile&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; PreprocessContent&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; file, IPreprocessor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt; preprocessors, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; preprocessors.&lt;span style=&quot;color: #0000FF;&quot;&gt;NullSafeAny&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;? preprocessors.&lt;span style=&quot;color: #0000FF;&quot;&gt;Aggregate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;content, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cntnt, pp&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; =&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var result = pp.&lt;span style=&quot;color: #0000FF;&quot;&gt;Process&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;file, cntnt&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bundleState.&lt;span style=&quot;color: #0000FF;&quot;&gt;DependentFiles&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;AddRange&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;result.&lt;span style=&quot;color: #0000FF;&quot;&gt;Dependencies&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; result.&lt;span style=&quot;color: #0000FF;&quot;&gt;Result&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;: content;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb45306&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Despite the fact that we have totally broken everything users have come to depend on, we really do want to make the transition easier for people who were using .less or coffeescript with SquishIt.  This is where the tremendous &lt;a href=&quot;http://nuget.org/packages/WebActivator&quot;&gt;WebActivator&lt;/a&gt; library comes in.  By including this library in our project, it allows us to define bits of code to run when the application starts up, like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb80938&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;assembly: WebActivator.&lt;span style=&quot;color: #0000FF;&quot;&gt;PreApplicationStartMethod&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=typeof+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;typeof&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;$rootnamespace$.&lt;span style=&quot;color: #0000FF;&quot;&gt;App_Start&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SquishItHogan&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Start&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; $rootnamespace$.&lt;span style=&quot;color: #0000FF;&quot;&gt;App_Start&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;Framework&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; SquishIt.&lt;span style=&quot;color: #0000FF;&quot;&gt;Hogan&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; SquishItHogan&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Start&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;RegisterScriptPreprocessor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; HoganPreprocessor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb56954&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks to this snippet, you don&#039;t actually need to do anything to hook up global preprocessing - just reference the dll containing your preprocessor and WebActivator.  This example is from the Hogan preprocessor, submitted by &lt;a href=&quot;https://twitter.com/jincod&quot;&gt;Abdrashitov Vadim&lt;/a&gt;.  This pull request made me smile more than any I&#039;ve seen in recent memory - a big part of the reason we moved to this model was to make it easier for people to define their own preprocessors and share them with the community.  To have one submitted by a user before we even had a production-ready release was just so cool.&lt;/p&gt;

&lt;p&gt;I think this covers most of the changes, at least at a cursory level.  I hope to find the time to put together a bit of proper documentation in the next few months, but hopefully this will help in the meantime.  I&#039;d like to extend a huge thanks to everyone who reported bugs in our pre-release versions, and to &lt;a href=&quot;https://twitter.com/rlsdumont&quot;&gt;Rodrigo Dumont&lt;/a&gt; who provided the spark to get started on this stuff late last year.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/preprocessor-extensibility-in-squishit-0-9&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>For the past couple years, .net developers have been embracing various content preprocessors as they become more accessible.  For the same couple of years, we've been trying to keep up.  The <a href="http://www.dotlesscss.org/">dotLess</a> port of the popular .less CSS extension has been getting better by leaps and bounds. It has become almost trivial to embed a javascript compiler in .net these days (thanks to projects like <a href="http://jurassic.codeplex.com/">Jurassic</a>), enabling us to support things like coffeescript.  So we're doing the obvious thing - stripping preprocessor support from our core library.</p>

<p>There are some good reasons for this.  Why force people to download things like Jurassic or dotLess if they don't have the need?  The flipside of this is that we'd been deliberately avoiding adding support for SASS/SCSS because of concerns about linking to IronRuby - these concerns largely disappear when preprocessing becomes an opt-in behavior.  Some of these libraries don't even work on Mono (I think .less might be the only one that works currently) so I feel extra bitter downloading code that won't run on my platform of choice.  Finally, the growth in adoption has been so fast that frankly, we're unable to keep up.</p>

<p>So let's take a look at some of the original code (well not original as some of our refactorings did find their way to the 0.8.x branch).</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb885'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb885','cb9173'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb885" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">internal</span> <span style="color: #0600FF;">override</span> <span style="color: #FF0000;">string</span> PreprocessForDebugging<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> filename<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>filename.<span style="color: #0000FF;">ToLower</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">EndsWith</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.coffee&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #FF0000;">string</span> js = ProcessCoffee<span style="color: #000000;">&#40;</span>filename<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; filename += debugExtension;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">using</span><span style="color: #000000;">&#40;</span>var fileWriter = fileWriterFactory.<span style="color: #0000FF;">GetFileWriter</span><span style="color: #000000;">&#40;</span>filename<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fileWriter.<span style="color: #0000FF;">Write</span><span style="color: #000000;">&#40;</span>js<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> filename;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb9173" style="display: none; color: red;"></div></div></div>

<p>As you can see, the trigger for preprocessing is the extension.  This is the desired behavior, but the way it was coded left it very brittle and made adding new preprocessors unwieldy.  So we set out to find a way to break this code out of the core library.  </p>

<p>The approach that we used was plugin based - we defined an interface and exposed a mechanism to register implementations of this interface with the core library.  Our original interface actually checked a file name to see if it needed preprocessing, so you could define any logic you wanted to determine whether to preprocess - we ended up eschewing this to go back to the extension-based decisions, for reasons that will be discussed later.  The interface looks like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb18249'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb18249','cb74739'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb18249" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">interface</span> IPreprocessor</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">bool</span> ValidFor<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> extension<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; IProcessResult Process<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> filePath, <span style="color: #FF0000;">string</span> content<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">string</span><span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> Extensions <span style="color: #000000;">&#123;</span> get; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb74739" style="display: none; color: red;"></div></div></div>

<p>The "ValidFor" method does exactly what it says - check if the preprocessor should be used with the supplied extension.  "Process" is where the actual preprocessing happens.  The array of extensions is exposed publicly to be used in registering the preprocessor - this is because each type of content bundle has a list of allowed extensions that is used to filter what gets included when we add a directory full of files.  Finally, the ProcessResult type includes a string representing preprocessed content and a list of any dependent files that were changed.  This last part was added by <a href="http://twitter.com/SimonPStevens">Simon Stevens</a> to enable <a href="https://github.com/jetheredge/SquishIt/pull/211">inclusion of .less imports as dependent files</a>.</p>

<p>Preprocessors can be registered two ways - both statically and with a particular bundle instance.  For the instance level configuration there is a method in the bundle's fluent API called "WithPreprocessor" that allows inclusion of a preprocessor with that bundle instance.  Globally, we used the static "Bundle" class to allow preprocessor registration - methods exist there for registering script, style, and global preprocessors.  If preprocessors of the same type are registered both statically and with a bundle instance, the instance-level preprocessor will be used.</p>

<p>Now, back to why we decided to make preprocessor selection based on extension rather than the complete file name.  To understand, I guess all you have to do is read about the <a href="http://guides.rubyonrails.org/asset_pipeline.html">Asset Pipeline</a> in Ruby on Rails, but I will attempt to summarize here.  The beautiful thing about the pipeline approach is the ability to chain preprocessing steps.  This allows you to use ERB's helper methods in your file <strong>prior to</strong> other preprocessing.  For example, if you wanted to use ERB helpers in a coffeescript file you can name your file file.js.coffee.erb - when an asset has the .coffee and .erb extensions, both preprocessors will be applied.  The order they are applied is driven by the reverse order of extensions, so *.coffee.erb would be preprocessed first by ERB and then by the coffeescript compiler.  Our goal was to emulate this behavior in SquishIt, and without matching preprocessors to extensions rather than filenames we wouldn't have been able to.</p>

<p>Enabling this behavior is mostly a matter of finding preprocessors correctly.  We find them like so:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb24544'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb24544','cb20656'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb24544" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">protected</span> IPreprocessor<span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> FindPreprocessors<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> file<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> file.<span style="color: #0000FF;">Split</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">'.'</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Skip</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">1</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Reverse</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Select</span><span style="color: #000000;">&#40;</span>FindPreprocessor<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Where</span><span style="color: #000000;">&#40;</span>p =&gt; p != <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">ToArray</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb20656" style="display: none; color: red;"></div></div></div>

<p>It's important to note here that "FindPreprocessor" uses the firstpreprocessor it finds for a given extension - so we need to take care if implementing preprocessors for common file extensions like ".js".  We can then use the preprocessors in the default order to process our content:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb57405'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb57405','cb20222'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb57405" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">protected</span> <span style="color: #FF0000;">string</span> PreprocessFile<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> file, IPreprocessor<span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> preprocessors<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> directoryWrapper.<span style="color: #0000FF;">ExecuteInDirectory</span><span style="color: #000000;">&#40;</span>Path.<span style="color: #0000FF;">GetDirectoryName</span><span style="color: #000000;">&#40;</span>file<span style="color: #000000;">&#41;</span>,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> =&gt; PreprocessContent<span style="color: #000000;">&#40;</span>file, preprocessors, ReadFile<span style="color: #000000;">&#40;</span>file<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">protected</span> <span style="color: #FF0000;">string</span> PreprocessContent<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> file, IPreprocessor<span style="color: #000000;">&#91;</span><span style="color: #000000;">&#93;</span> preprocessors, <span style="color: #FF0000;">string</span> content<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> preprocessors.<span style="color: #0000FF;">NullSafeAny</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;? preprocessors.<span style="color: #0000FF;">Aggregate</span><span style="color: #000000;">&#40;</span>content, <span style="color: #000000;">&#40;</span>cntnt, pp<span style="color: #000000;">&#41;</span> =&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var result = pp.<span style="color: #0000FF;">Process</span><span style="color: #000000;">&#40;</span>file, cntnt<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bundleState.<span style="color: #0000FF;">DependentFiles</span>.<span style="color: #0000FF;">AddRange</span><span style="color: #000000;">&#40;</span>result.<span style="color: #0000FF;">Dependencies</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> result.<span style="color: #0000FF;">Result</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: content;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb20222" style="display: none; color: red;"></div></div></div>

<p>Despite the fact that we have totally broken everything users have come to depend on, we really do want to make the transition easier for people who were using .less or coffeescript with SquishIt.  This is where the tremendous <a href="http://nuget.org/packages/WebActivator">WebActivator</a> library comes in.  By including this library in our project, it allows us to define bits of code to run when the application starts up, like so:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb31497'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb31497','cb45316'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb31497" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>assembly: WebActivator.<span style="color: #0000FF;">PreApplicationStartMethod</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=typeof+msdn.microsoft.com"><span style="color: #008000;">typeof</span></a><span style="color: #000000;">&#40;</span>$rootnamespace$.<span style="color: #0000FF;">App_Start</span>.<span style="color: #0000FF;">SquishItHogan</span><span style="color: #000000;">&#41;</span>, <span style="color: #808080;">&quot;Start&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">namespace</span> $rootnamespace$.<span style="color: #0000FF;">App_Start</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> SquishIt.<span style="color: #0000FF;">Framework</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> SquishIt.<span style="color: #0000FF;">Hogan</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> SquishItHogan</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> Start<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Bundle.<span style="color: #0000FF;">RegisterScriptPreprocessor</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> HoganPreprocessor<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb45316" style="display: none; color: red;"></div></div></div>

<p>Thanks to this snippet, you don't actually need to do anything to hook up global preprocessing - just reference the dll containing your preprocessor and WebActivator.  This example is from the Hogan preprocessor, submitted by <a href="https://twitter.com/jincod">Abdrashitov Vadim</a>.  This pull request made me smile more than any I've seen in recent memory - a big part of the reason we moved to this model was to make it easier for people to define their own preprocessors and share them with the community.  To have one submitted by a user before we even had a production-ready release was just so cool.</p>

<p>I think this covers most of the changes, at least at a cursory level.  I hope to find the time to put together a bit of proper documentation in the next few months, but hopefully this will help in the meantime.  I'd like to extend a huge thanks to everyone who reported bugs in our pre-release versions, and to <a href="https://twitter.com/rlsdumont">Rodrigo Dumont</a> who provided the spark to get started on this stuff late last year.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/preprocessor-extensibility-in-squishit-0-9">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/preprocessor-extensibility-in-squishit-0-9#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1771</wfw:commentRss>
		</item>
				<item>
			<title>SquishIt Integration with Amazon S3 / Cloudfront</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/making-squishit-work-with-amazon</link>
			<pubDate>Tue, 12 Jun 2012 11:00:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>			<guid isPermaLink="false">1751@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;For the unfortunate souls not in the know (or is it the fortunate souls using one of the myriad alternatives?), &lt;a href=&quot;https://github.com/jetheredge/SquishIt&quot;&gt;SquishIt&lt;/a&gt; is a library used to optimize content delivery at runtime in ASP.net applications.  It combines and minifies javascript files, and also does a bit of preprocessing for things like &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt; and &lt;a href=&quot;http://coffeescript.org/&quot;&gt;CoffeeScript&lt;/a&gt;.  I&#039;ve been working with Justin Etheredge (&lt;a href=&quot;http://www.codethinked.com/&quot;&gt;blog&lt;/a&gt;|&lt;a href=&quot;https://twitter.com/#!/justinetheredge&quot;&gt;twitter&lt;/a&gt;) on this for a while, first on patches for various bugs I encountered trying to use SquishIt on linux, but more recently my focus has been on improving extensibility.  One of the areas that I really felt the library could benefit from increased extensibility is the area of CDN support.  I&#039;ve been doing a lot of work with Amazon CloudFront lately, and decided it would be cool to see how cleanly I could get SquishIt to work with the service.&lt;/p&gt;

&lt;h3&gt;SquishIt CDN Support in Previous Versions&lt;/h3&gt;

&lt;p&gt;I suppose it makes sense to start with what we already had in place.  CDN support in SquishIt has been slowly progressing, largely thanks to community contributions.  As of version 0.8.6 we did have support for injecting a base URL into asset paths, but this required you to know the generated file name in advance and upload it to your CDN through other means.  While this worked, and could be easily automated in your build process, it wasn&#039;t exactly convenient.  The pull requests we&#039;ve gotten have done a fairly good job showing us what the community wants in terms of CDN support, so it feels like it is time to start trying to treat it as a first class citizen.    The first thing I would like is a way to render the combined file directly to my CDN if it doesn&#039;t already exist, and maybe a way to force overwriting the file if need be.&lt;/p&gt;

&lt;h3&gt;Adding Support for Custom Renderers&lt;/h3&gt;

&lt;p&gt;For a while now, SquishIt has had an IRenderer interface.  It has been there, but it has only been used internally to support rendering to the file system or to an in-memory cache.  To get started, we needed to expose this interface publicly so that other assemblies could provide implementations.  Once exposed, we need to enable consumers to supply their custom renderers to the SquishIt core somehow.&lt;/p&gt;

&lt;p&gt;SquishIt uses a fluent configuration syntax for setting up individual bundles, and that seemed as good a place to start as any.  Typical usage looks something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb87957&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;JavaScript&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithAttribute&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;attrName&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;attrValue&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;file1.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/otherscripts/file2.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Render&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;combinedOutput.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb33200&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So the first thing that came to mind was to add something like a &quot;WithFileRenderer&quot; method.  This would give us a way to inject a renderer into a bundle and have it used to render the combined files.  However, we probably don&#039;t want to render to the CDN while debugging, so &quot;WithReleaseFileRenderer&quot; might be more appropriate.   Setting up the method went something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb47176&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;IRenderer releaseFileRenderer;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; T WithReleaseFileRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IRenderer renderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;releaseFileRenderer&lt;/span&gt; = renderer;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;T&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb45711&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We also want a way to configure this globally, to do that we needed to add a bit to our configuration class.  This was basically the same thing:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb56494&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;IRenderer _defaultReleaseRenderer;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; Configuration UseReleaseRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IRenderer releaseRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _defaultReleaseRenderer = releaseRenderer;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb9762&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we need to change the way the file renderer is obtained when we go to render the combined assets.  Previously we were instantiating a new FileRenderer or CacheRenderer depending on circumstance, and passing that renderer into the main rendering method.  This won&#039;t cut it anymore, as our needs have gotten significantly more complex.  The constraints we have to deal with are as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When debugging we should use a normal file renderer&lt;/li&gt;
  &lt;li&gt;We should favor a renderer configured at the instance level over one configured statically&lt;/li&gt;
  &lt;li&gt;If no instance or static renderer is configured we should use the old default behavior&lt;/li&gt; 
&lt;/ul&gt;

&lt;p&gt;So the constructor calls for a new FileRenderer are replaced with calls to this method:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb77560&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; IRenderer GetFileRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; debugStatusReader.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDebuggingEnabled&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; ? &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FileRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;fileWriterFactory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; :&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bundleState.&lt;span style=&quot;color: #0000FF;&quot;&gt;ReleaseFileRenderer&lt;/span&gt; ??&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Configuration.&lt;span style=&quot;color: #0000FF;&quot;&gt;Instance&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;DefaultReleaseRenderer&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; ??&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FileRenderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;fileWriterFactory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb34426&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is basically all we needed to do to enable us to plug in a custom renderer to use in release mode.  Now we can look at how we can make the CDN integration happen.&lt;/p&gt;

&lt;h3&gt;Building the S3 Keys&lt;/h3&gt;

&lt;p&gt;The only really tricky thing about building the renderer is that it takes a string representing the disk location to render to.  Changing what the renderer takes as a parameter would involve a more significant change to the core behavior than I&#039;m comfortable making right now, so the first thing we need is a way to turn these disk locations into keys that S3 can use.  The key we create needs to match the relative path to that of the locally-rendered asset so that injecting the base url will yield the absolute path that we need.&lt;/p&gt;

&lt;p&gt;None of this is terribly difficult - the main edge cases we need to cover are&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Root appearing twice in the file path (because of Windows&#039; drive lettering this is mostly an issue running on unix-based systems)&lt;/li&gt;
  &lt;li&gt;Virtual directories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To meet these requirements the two pieces of information that we need inside the key builder are the physical application path and virtual directory.  Here are some tests:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb83344&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ReturnToRelative&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var root = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;C:\fake\dir\&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var file = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;another\file.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var expected = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;another/file.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var builder = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; KeyBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;expected, builder.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetKeyFor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root + file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ReturnToRelative_Injects_Virtual_Directory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var root = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;C:\fake\dir\&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var file = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;another\file.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var vdir = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/this&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var expected = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;this/another/file.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var builder = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; KeyBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root, vdir&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;expected, builder.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetKeyFor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root + file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ReturnToRelative_Only_Replaces_First_Occurrence_Of_Root&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var root = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;test/&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var file = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;another/andthen/test/again.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var expected = &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;another/andthen/test/again.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var builder = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; KeyBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;expected, builder.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetKeyFor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;root + file&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb75471&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the implementation for the KeyBuilder:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb93127&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;internal&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; KeyBuilder : IKeyBuilder&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; physicalApplicationPath;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; virtualDirectory;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; KeyBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; physicalApplicationPath, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; virtualDirectory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;physicalApplicationPath&lt;/span&gt; = physicalApplicationPath;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;virtualDirectory&lt;/span&gt; = virtualDirectory;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; GetKeyFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; path&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; RelativeFromAbsolutePath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;path&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;TrimStart&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; RelativeFromAbsolutePath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; path&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; path = path.&lt;span style=&quot;color: #0000FF;&quot;&gt;StartsWith&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;physicalApplicationPath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ? path.&lt;span style=&quot;color: #0000FF;&quot;&gt;Substring&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;physicalApplicationPath.&lt;span style=&quot;color: #0000FF;&quot;&gt;Length&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : path;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; virtualDirectory + &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt; + path.&lt;span style=&quot;color: #0000FF;&quot;&gt;Replace&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;@&quot;\&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;TrimStart&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb93408&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have a means to build keys we can look at implementing the S3 Renderer.&lt;/p&gt;

&lt;h3&gt;S3 Renderer Implementation&lt;/h3&gt;

&lt;p&gt;The interface for renderers is very simple.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb89827&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;interface&lt;/span&gt; IRenderer&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Render&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; outputPath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb59493&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only things we&#039;ll need to implement this method are an initialized S3 client, a bucket and the key builder we implemented in the last section.  By default, we won&#039;t want to upload our content if it already exists on the CDN, so we will need to check for existence before uploading the content.  This can be done by querying for object metadata using the desired key - if the file doesn&#039;t exist we will get a &quot;not found&quot; status on the exception thrown by the s3 client.  So the most important test will look like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb62653&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Render_Uploads_If_File_Doesnt_Exist&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var s3client = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Mock&amp;lt;AmazonS3&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var keyBuilder = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Mock&amp;lt;IKeyBuilder&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var key = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;key&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var bucket = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;bucket&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var path = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;path&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var content = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;content&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; keyBuilder.&lt;span style=&quot;color: #0000FF;&quot;&gt;Setup&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;kb =&amp;gt; kb.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetKeyFor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;path&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Returns&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; s3client.&lt;span style=&quot;color: #0000FF;&quot;&gt;Setup&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;c =&amp;gt; c.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetObjectMetadata&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;It.&lt;span style=&quot;color: #0000FF;&quot;&gt;Is&lt;/span&gt;&amp;lt;GetObjectMetadataRequest&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;gomr =&amp;gt; gomr.&lt;span style=&quot;color: #0000FF;&quot;&gt;BucketName&lt;/span&gt; == bucket &amp;amp;&amp;amp; gomr.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; == key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0000FF;&quot;&gt;Throws&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; AmazonS3Exception&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;, HttpStatusCode.&lt;span style=&quot;color: #0000FF;&quot;&gt;NotFound&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;var renderer = S3Renderer.&lt;span style=&quot;color: #0000FF;&quot;&gt;Create&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3client.&lt;span style=&quot;color: #FF0000;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithKeyBuilder&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;keyBuilder.&lt;span style=&quot;color: #FF0000;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; renderer.&lt;span style=&quot;color: #0000FF;&quot;&gt;Render&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;content, path&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; s3client.&lt;span style=&quot;color: #0000FF;&quot;&gt;Verify&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;c =&amp;gt; c.&lt;span style=&quot;color: #0000FF;&quot;&gt;PutObject&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;It.&lt;span style=&quot;color: #0000FF;&quot;&gt;Is&lt;/span&gt;&amp;lt;PutObjectRequest&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;por =&amp;gt; por.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; == key &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; por.&lt;span style=&quot;color: #0000FF;&quot;&gt;BucketName&lt;/span&gt; == bucket &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; por.&lt;span style=&quot;color: #0000FF;&quot;&gt;ContentBody&lt;/span&gt; == content &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; por.&lt;span style=&quot;color: #0000FF;&quot;&gt;CannedACL&lt;/span&gt; == S3CannedACL.&lt;span style=&quot;color: #0000FF;&quot;&gt;NoACL&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb19938&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that it is checking the PutObjectRequest to ensure that the ACL used is &quot;NoACL&quot;.  This is probably not an optimal default (most people will want the &quot;PublicRead&quot; ACL I imagine) but I decided to err on the side of caution and force people to opt-in to making their content publicly visible.  The implementation for the render method looks something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb15233&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Render&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; outputPath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;outputPath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; || &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;throw&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; InvalidOperationException&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Can&#039;t render to S3 with missing key/content.&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var key = keyBuilder.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetKeyFor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;outputPath&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!FileExists&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; UploadContent&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key, content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; UploadContent&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; key, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; PutObjectRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithKey&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithCannedACL&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cannedACL&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithContentBody&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;content&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; s3client.&lt;span style=&quot;color: #0000FF;&quot;&gt;PutObject&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; FileExists&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;try&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; GetObjectMetadataRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithKey&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var response = s3client.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetObjectMetadata&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;true&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;AmazonS3Exception ex&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ex.&lt;span style=&quot;color: #0000FF;&quot;&gt;StatusCode&lt;/span&gt; == HttpStatusCode.&lt;span style=&quot;color: #0000FF;&quot;&gt;NotFound&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;throw&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb33090&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It has gotten slightly more complex since then (I&#039;ve added configurable headers and an option for forcing overwrite of the existing file) but the core logic remains the same.  It&#039;s a fairly naive implementation but my experience with the Amazon services has been good enough so far that I haven&#039;t encountered a lot of the exceptions that I hope to add handling for in the future.&lt;/p&gt;

&lt;h3&gt;Adding Invalidation&lt;/h3&gt;

&lt;p&gt;At this point we should be able to render our content directly to S3, but this doesn&#039;t get us all the way to where we need to be.  While hosting static content in S3 offers some advantages over hosting it locally and it &lt;strong&gt;can&lt;/strong&gt; work as a CDN, using CloudFront to deliver your S3 content makes more sense if you really want to minimize latency.  To make this work we&#039;ll just need to add an invaliator to the mix.  A test for the core usage looks like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb82591&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Invalidate&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var cloudfrontClient = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Mock&amp;lt;AmazonCloudFront&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var distributionId = Guid.&lt;span style=&quot;color: #0000FF;&quot;&gt;NewGuid&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var bucket = Guid.&lt;span style=&quot;color: #0000FF;&quot;&gt;NewGuid&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var distribution = bucket + &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.s3.amazonaws.com&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var key = Guid.&lt;span style=&quot;color: #0000FF;&quot;&gt;NewGuid&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var listDistributionsResponse = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ListDistributionsResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; listDistributionsResponse.&lt;span style=&quot;color: #0000FF;&quot;&gt;Distribution&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CloudFrontDistribution&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Id = distributionId,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; DistributionConfig = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CloudFrontDistributionConfig&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; S3Origin = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; S3Origin&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;distribution, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; cloudfrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;Setup&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cfc =&amp;gt; cfc.&lt;span style=&quot;color: #0000FF;&quot;&gt;ListDistributions&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Returns&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;listDistributionsResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var invalidator = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CloudFrontInvalidator&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cloudfrontClient.&lt;span style=&quot;color: #FF0000;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; invalidator.&lt;span style=&quot;color: #0000FF;&quot;&gt;InvalidateObject&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucket, key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; cloudfrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;Verify&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cfc =&amp;gt; cfc.&lt;span style=&quot;color: #0000FF;&quot;&gt;PostInvalidation&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;It.&lt;span style=&quot;color: #0000FF;&quot;&gt;Is&lt;/span&gt;&amp;lt;PostInvalidationRequest&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;pir =&amp;gt; pir.&lt;span style=&quot;color: #0000FF;&quot;&gt;DistributionId&lt;/span&gt; == distributionId&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; pir.&lt;span style=&quot;color: #0000FF;&quot;&gt;InvalidationBatch&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Paths&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; == &lt;span style=&quot;color: #FF0000;&quot;&gt;1&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; pir.&lt;span style=&quot;color: #0000FF;&quot;&gt;InvalidationBatch&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Paths&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;First&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; == key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb77043&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The implementation is pretty straightforward, and will look very familiar to anyone who read my post regarding &lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api&quot;&gt;copying buckets with the S3 API&lt;/a&gt;.  There are only two changes, first that we only need to invalidate one object at a time, and second that we only want to query for the list of CloudFront distributions once.  Code for the CloudFront invalidator looks like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb46098&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; CloudFrontInvalidator : IDisposable, IInvalidator&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;const&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; amazonBucketUriSuffix = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.s3.amazonaws.com&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;const&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; dateFormatWithMilliseconds = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;yyyy-MM-dd hh:mm:ss.ff&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; AmazonCloudFront cloudFrontClient;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; CloudFrontInvalidator&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;AmazonCloudFront cloudFrontClient&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;cloudFrontClient&lt;/span&gt; = cloudFrontClient;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; InvalidateObject&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucket, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; key&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var distId = GetDistributionIdFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;distId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var invalidationRequest = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; PostInvalidationRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDistribtionId&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;distId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithInvalidationBatch&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; InvalidationBatch&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;DateTime.&lt;span style=&quot;color: #0000FF;&quot;&gt;Now&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;dateFormatWithMilliseconds&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; List&amp;lt;string&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; key &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cloudFrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;PostInvalidation&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;invalidationRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Dictionary&amp;lt;string, string&amp;gt; distributionNameAndIds;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; GetDistributionIdFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; distributionNameAndIds = distributionNameAndIds ??&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cloudFrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;ListDistributions&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Distribution&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;ToDictionary&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cfd =&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cfd.&lt;span style=&quot;color: #0000FF;&quot;&gt;DistributionConfig&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;S3Origin&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;DNSName&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Replace&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;amazonBucketUriSuffix, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cfd =&amp;gt; cfd.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; id = &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; distributionNameAndIds.&lt;span style=&quot;color: #0000FF;&quot;&gt;TryGetValue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName, &lt;span style=&quot;color: #0600FF;&quot;&gt;out&lt;/span&gt; id&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; id;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Dispose&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cloudFrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;Dispose&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb81191&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As an interesting aside, while I was working on this Amazon released &lt;a href=&quot;http://aws.amazon.com/releasenotes/7875688065681094&quot;&gt;support for querystring invalidation/versioning&lt;/a&gt;, which is SquishIt&#039;s default behavior.  I had planned to add a release note telling people that they would need to use squishit&#039;s &quot;hash in filename&quot; option, but it seems like now there won&#039;t be any need &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;

&lt;h3&gt;Neat, But How Do I Use This?&lt;/h3&gt;

&lt;p&gt;It&#039;s nice that this all works on paper (and in unit tests) but how do we actually tie everything together?  One of the key design decisions was that the renderer is instantiated with pre-initialized CloudFront and S3 clients.  This way users aren&#039;t locked into a certain method of getting credentials or anything like that.  To use the custom renderer for only a particular bundle usage would be something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb61895&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;var s3client = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; AmazonS3Client&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;accessKey&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;secretKey&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;var renderer = S3Renderer.&lt;span style=&quot;color: #0000FF;&quot;&gt;Create&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3client&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;bucket&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDefaultKeyBuilder&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;HttpContext.&lt;span style=&quot;color: #0000FF;&quot;&gt;Current&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Request&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;PhysicalApplicationPath&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; HttpContext.&lt;span style=&quot;color: #0000FF;&quot;&gt;Current&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Request&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithCannedAcl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;S3CannedACL.&lt;span style=&quot;color: #0000FF;&quot;&gt;PublicRead&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;as&lt;/span&gt; IRenderer;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;JavaScript&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithReleaseFileRenderer&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;renderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithOutputBaseHref&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://s3.amazonaws.com/bucket&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;file1.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;file2.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Render&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;combined.js&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb17432&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is nice, but I think the global configuration is probably what people will be using more often.  As is common in ASP.net apps a lot of the setup magic for this happens in the app initialization.  So you&#039;d add something like this to your Application_Start method (in Global.asax.cs):&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb87574&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;var s3client = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; AmazonS3Client&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;accessKey&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;secretKey&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;var renderer = S3Renderer.&lt;span style=&quot;color: #0000FF;&quot;&gt;Create&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3client&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;bucket&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDefaultKeyBuilder&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;HttpContext.&lt;span style=&quot;color: #0000FF;&quot;&gt;Current&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Request&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;PhysicalApplicationPath&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; HttpContext.&lt;span style=&quot;color: #0000FF;&quot;&gt;Current&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Request&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithCannedAcl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;S3CannedACL.&lt;span style=&quot;color: #0000FF;&quot;&gt;PublicRead&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;as&lt;/span&gt; IRenderer;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Bundle.&lt;span style=&quot;color: #0000FF;&quot;&gt;ConfigureDefaults&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;UseReleaseRenderer&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;renderer&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;UseDefaultOutputBaseHref&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://s3.amazonaws.com/bucket&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb89892&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I tried to make this something that could be run via WebActivator, but had trouble finding a method to use that would have access to the HttpContext (needed to resolve application path and virtual directory) so for now it needs to be set up manually.  This may be for the best though, as it doesn&#039;t force any particular convention for access key / secret key retrieval.  It doesn&#039;t &lt;strong&gt;feel&lt;/strong&gt; like a ton of setup code to me, hopefully others will agree.&lt;/p&gt;

&lt;h3&gt;What&#039;s Next&lt;/h3&gt;

&lt;p&gt;Now that SquishIt 0.8.7 has been released I can finally start planning to make this available &lt;a href=&quot;http://nuget.org/packages/SquishIt.S3&quot;&gt;on NuGet&lt;/a&gt; as a standard package (currently in beta until I get a little more testing).  It can be installed like any other, but will require updating SquishIt if you&#039;re using a pre-0.8.7 version.  If you need to report any issues encountered while using the library, or feel like contributing some code, please do so &lt;a href=&quot;https://github.com/AlexCuse/SquishIt.S3&quot;&gt;on github&lt;/a&gt;.  Oh, and if you just want to kick the tires on SquishIt without all this other nonsense, or try making it work with another CDN you can find the core library &lt;a href=&quot;http://nuget.org/packages/SquishIt&quot;&gt;on NuGet&lt;/a&gt; as well.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/making-squishit-work-with-amazon&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>For the unfortunate souls not in the know (or is it the fortunate souls using one of the myriad alternatives?), <a href="https://github.com/jetheredge/SquishIt">SquishIt</a> is a library used to optimize content delivery at runtime in ASP.net applications.  It combines and minifies javascript files, and also does a bit of preprocessing for things like <a href="http://lesscss.org/">LESS</a> and <a href="http://coffeescript.org/">CoffeeScript</a>.  I've been working with Justin Etheredge (<a href="http://www.codethinked.com/">blog</a>|<a href="https://twitter.com/#!/justinetheredge">twitter</a>) on this for a while, first on patches for various bugs I encountered trying to use SquishIt on linux, but more recently my focus has been on improving extensibility.  One of the areas that I really felt the library could benefit from increased extensibility is the area of CDN support.  I've been doing a lot of work with Amazon CloudFront lately, and decided it would be cool to see how cleanly I could get SquishIt to work with the service.</p>

<h3>SquishIt CDN Support in Previous Versions</h3>

<p>I suppose it makes sense to start with what we already had in place.  CDN support in SquishIt has been slowly progressing, largely thanks to community contributions.  As of version 0.8.6 we did have support for injecting a base URL into asset paths, but this required you to know the generated file name in advance and upload it to your CDN through other means.  While this worked, and could be easily automated in your build process, it wasn't exactly convenient.  The pull requests we've gotten have done a fairly good job showing us what the community wants in terms of CDN support, so it feels like it is time to start trying to treat it as a first class citizen.    The first thing I would like is a way to render the combined file directly to my CDN if it doesn't already exist, and maybe a way to force overwriting the file if need be.</p>

<h3>Adding Support for Custom Renderers</h3>

<p>For a while now, SquishIt has had an IRenderer interface.  It has been there, but it has only been used internally to support rendering to the file system or to an in-memory cache.  To get started, we needed to expose this interface publicly so that other assemblies could provide implementations.  Once exposed, we need to enable consumers to supply their custom renderers to the SquishIt core somehow.</p>

<p>SquishIt uses a fluent configuration syntax for setting up individual bundles, and that seemed as good a place to start as any.  Typical usage looks something like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb45559'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb45559','cb84858'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb45559" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Bundle.<span style="color: #0000FF;">JavaScript</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithAttribute</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;attrName&quot;</span>, <span style="color: #808080;">&quot;attrValue&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;file1.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;/otherscripts/file2.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">Render</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;combinedOutput.js&quot;</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb84858" style="display: none; color: red;"></div></div></div>

<p>So the first thing that came to mind was to add something like a "WithFileRenderer" method.  This would give us a way to inject a renderer into a bundle and have it used to render the combined files.  However, we probably don't want to render to the CDN while debugging, so "WithReleaseFileRenderer" might be more appropriate.   Setting up the method went something like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb81810'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb81810','cb7942'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb81810" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">IRenderer releaseFileRenderer;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">public</span> T WithReleaseFileRenderer<span style="color: #000000;">&#40;</span>IRenderer renderer<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">releaseFileRenderer</span> = renderer;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #000000;">&#40;</span>T<span style="color: #000000;">&#41;</span><span style="color: #0600FF;">this</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb7942" style="display: none; color: red;"></div></div></div>

<p>We also want a way to configure this globally, to do that we needed to add a bit to our configuration class.  This was basically the same thing:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb89993'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb89993','cb60782'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb89993" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">IRenderer _defaultReleaseRenderer;</li><li style="" class="li2"><span style="color: #0600FF;">public</span> Configuration UseReleaseRenderer<span style="color: #000000;">&#40;</span>IRenderer releaseRenderer<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; _defaultReleaseRenderer = releaseRenderer;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #0600FF;">this</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb60782" style="display: none; color: red;"></div></div></div>

<p>Finally, we need to change the way the file renderer is obtained when we go to render the combined assets.  Previously we were instantiating a new FileRenderer or CacheRenderer depending on circumstance, and passing that renderer into the main rendering method.  This won't cut it anymore, as our needs have gotten significantly more complex.  The constraints we have to deal with are as follows:</p>

<ul>
  <li>When debugging we should use a normal file renderer</li>
  <li>We should favor a renderer configured at the instance level over one configured statically</li>
  <li>If no instance or static renderer is configured we should use the old default behavior</li> 
</ul>

<p>So the constructor calls for a new FileRenderer are replaced with calls to this method:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb42169'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb42169','cb17176'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb42169" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">protected</span> IRenderer GetFileRenderer<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> debugStatusReader.<span style="color: #0000FF;">IsDebuggingEnabled</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> ? <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FileRenderer<span style="color: #000000;">&#40;</span>fileWriterFactory<span style="color: #000000;">&#41;</span> :</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; bundleState.<span style="color: #0000FF;">ReleaseFileRenderer</span> ??</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Configuration.<span style="color: #0000FF;">Instance</span>.<span style="color: #0000FF;">DefaultReleaseRenderer</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> ??</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FileRenderer<span style="color: #000000;">&#40;</span>fileWriterFactory<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb17176" style="display: none; color: red;"></div></div></div>

<p>This is basically all we needed to do to enable us to plug in a custom renderer to use in release mode.  Now we can look at how we can make the CDN integration happen.</p>

<h3>Building the S3 Keys</h3>

<p>The only really tricky thing about building the renderer is that it takes a string representing the disk location to render to.  Changing what the renderer takes as a parameter would involve a more significant change to the core behavior than I'm comfortable making right now, so the first thing we need is a way to turn these disk locations into keys that S3 can use.  The key we create needs to match the relative path to that of the locally-rendered asset so that injecting the base url will yield the absolute path that we need.</p>

<p>None of this is terribly difficult - the main edge cases we need to cover are</p>

<ul>
  <li>Root appearing twice in the file path (because of Windows' drive lettering this is mostly an issue running on unix-based systems)</li>
  <li>Virtual directories</li>
</ul>

<p>To meet these requirements the two pieces of information that we need inside the key builder are the physical application path and virtual directory.  Here are some tests:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb76978'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb76978','cb91459'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb76978" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ReturnToRelative<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var root = <span style="color: #808080;">@"C:\fake\dir\&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var file = <span style="color: #808080;">@"another\file.js&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var expected = <span style="color: #808080;">@"another/file.js&quot;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var builder = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> KeyBuilder<span style="color: #000000;">&#40;</span>root, <span style="color: #808080;">&quot;&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span>expected, builder.<span style="color: #0000FF;">GetKeyFor</span><span style="color: #000000;">&#40;</span>root + file<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ReturnToRelative_Injects_Virtual_Directory<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var root = <span style="color: #808080;">@"C:\fake\dir\&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var file = <span style="color: #808080;">@"another\file.js&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var vdir = <span style="color: #808080;">&quot;/this&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var expected = <span style="color: #808080;">@"this/another/file.js&quot;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var builder = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> KeyBuilder<span style="color: #000000;">&#40;</span>root, vdir<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span>expected, builder.<span style="color: #0000FF;">GetKeyFor</span><span style="color: #000000;">&#40;</span>root + file<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ReturnToRelative_Only_Replaces_First_Occurrence_Of_Root<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var root = <span style="color: #808080;">@"test/&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var file = <span style="color: #808080;">@"another/andthen/test/again.js&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var expected = <span style="color: #808080;">@"another/andthen/test/again.js&quot;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var builder = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> KeyBuilder<span style="color: #000000;">&#40;</span>root, <span style="color: #808080;">&quot;&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span>expected, builder.<span style="color: #0000FF;">GetKeyFor</span><span style="color: #000000;">&#40;</span>root + file<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb91459" style="display: none; color: red;"></div></div></div>

<p>And the implementation for the KeyBuilder:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb47039'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb47039','cb78332'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb47039" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">internal</span> <span style="color: #FF0000;">class</span> KeyBuilder : IKeyBuilder</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> <span style="color: #FF0000;">string</span> physicalApplicationPath;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> <span style="color: #FF0000;">string</span> virtualDirectory;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> KeyBuilder<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> physicalApplicationPath, <span style="color: #FF0000;">string</span> virtualDirectory<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">physicalApplicationPath</span> = physicalApplicationPath;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">virtualDirectory</span> = virtualDirectory;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">string</span> GetKeyFor<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> path<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> RelativeFromAbsolutePath<span style="color: #000000;">&#40;</span>path<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">TrimStart</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">'/'</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">string</span> RelativeFromAbsolutePath<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> path<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; path = path.<span style="color: #0000FF;">StartsWith</span><span style="color: #000000;">&#40;</span>physicalApplicationPath<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ? path.<span style="color: #0000FF;">Substring</span><span style="color: #000000;">&#40;</span>physicalApplicationPath.<span style="color: #0000FF;">Length</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : path;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> virtualDirectory + <span style="color: #808080;">&quot;/&quot;</span> + path.<span style="color: #0000FF;">Replace</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">@"\&quot;</span>, <span style="color: #808080;">&quot;/&quot;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">TrimStart</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">'/'</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb78332" style="display: none; color: red;"></div></div></div>

<p>Now that we have a means to build keys we can look at implementing the S3 Renderer.</p>

<h3>S3 Renderer Implementation</h3>

<p>The interface for renderers is very simple.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb46123'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb46123','cb77006'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb46123" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">interface</span> IRenderer</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">void</span> Render<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> content, <span style="color: #FF0000;">string</span> outputPath<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb77006" style="display: none; color: red;"></div></div></div>

<p>The only things we'll need to implement this method are an initialized S3 client, a bucket and the key builder we implemented in the last section.  By default, we won't want to upload our content if it already exists on the CDN, so we will need to check for existence before uploading the content.  This can be done by querying for object metadata using the desired key - if the file doesn't exist we will get a "not found" status on the exception thrown by the s3 client.  So the most important test will look like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb53784'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb53784','cb20270'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb53784" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Render_Uploads_If_File_Doesnt_Exist<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var s3client = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Mock&lt;AmazonS3&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var keyBuilder = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Mock&lt;IKeyBuilder&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var key = <span style="color: #808080;">&quot;key&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var bucket = <span style="color: #808080;">&quot;bucket&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var path = <span style="color: #808080;">&quot;path&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var content = <span style="color: #808080;">&quot;content&quot;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; keyBuilder.<span style="color: #0000FF;">Setup</span><span style="color: #000000;">&#40;</span>kb =&gt; kb.<span style="color: #0000FF;">GetKeyFor</span><span style="color: #000000;">&#40;</span>path<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Returns</span><span style="color: #000000;">&#40;</span>key<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; s3client.<span style="color: #0000FF;">Setup</span><span style="color: #000000;">&#40;</span>c =&gt; c.<span style="color: #0000FF;">GetObjectMetadata</span><span style="color: #000000;">&#40;</span>It.<span style="color: #0000FF;">Is</span>&lt;GetObjectMetadataRequest&gt;<span style="color: #000000;">&#40;</span>gomr =&gt; gomr.<span style="color: #0000FF;">BucketName</span> == bucket &amp;&amp; gomr.<span style="color: #0000FF;">Key</span> == key<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000FF;">Throws</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> AmazonS3Exception<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;&quot;</span>, HttpStatusCode.<span style="color: #0000FF;">NotFound</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span><span style="color: #000000;">&#40;</span>var renderer = S3Renderer.<span style="color: #0000FF;">Create</span><span style="color: #000000;">&#40;</span>s3client.<span style="color: #FF0000;">Object</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>bucket<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithKeyBuilder</span><span style="color: #000000;">&#40;</span>keyBuilder.<span style="color: #FF0000;">Object</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; renderer.<span style="color: #0000FF;">Render</span><span style="color: #000000;">&#40;</span>content, path<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; s3client.<span style="color: #0000FF;">Verify</span><span style="color: #000000;">&#40;</span>c =&gt; c.<span style="color: #0000FF;">PutObject</span><span style="color: #000000;">&#40;</span>It.<span style="color: #0000FF;">Is</span>&lt;PutObjectRequest&gt;<span style="color: #000000;">&#40;</span>por =&gt; por.<span style="color: #0000FF;">Key</span> == key &amp;&amp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; por.<span style="color: #0000FF;">BucketName</span> == bucket &amp;&amp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; por.<span style="color: #0000FF;">ContentBody</span> == content &amp;&amp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; por.<span style="color: #0000FF;">CannedACL</span> == S3CannedACL.<span style="color: #0000FF;">NoACL</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb20270" style="display: none; color: red;"></div></div></div>

<p>Note that it is checking the PutObjectRequest to ensure that the ACL used is "NoACL".  This is probably not an optimal default (most people will want the "PublicRead" ACL I imagine) but I decided to err on the side of caution and force people to opt-in to making their content publicly visible.  The implementation for the render method looks something like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb36026'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb36026','cb56374'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb36026" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Render<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> content, <span style="color: #FF0000;">string</span> outputPath<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #000000;">&#40;</span>outputPath<span style="color: #000000;">&#41;</span> || <span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #000000;">&#40;</span>content<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #0600FF;">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> InvalidOperationException<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Can't render to S3 with missing key/content.&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var key = keyBuilder.<span style="color: #0000FF;">GetKeyFor</span><span style="color: #000000;">&#40;</span>outputPath<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>!FileExists<span style="color: #000000;">&#40;</span>key<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; UploadContent<span style="color: #000000;">&#40;</span>key, content<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #0600FF;">void</span> UploadContent<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> key, <span style="color: #FF0000;">string</span> content<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> PutObjectRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>bucket<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithKey</span><span style="color: #000000;">&#40;</span>key<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithCannedACL</span><span style="color: #000000;">&#40;</span>cannedACL<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithContentBody</span><span style="color: #000000;">&#40;</span>content<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; s3client.<span style="color: #0000FF;">PutObject</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #FF0000;">bool</span> FileExists<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> key<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">try</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> GetObjectMetadataRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>bucket<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithKey</span><span style="color: #000000;">&#40;</span>key<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var response = s3client.<span style="color: #0000FF;">GetObjectMetadata</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #0600FF;">true</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">catch</span><span style="color: #000000;">&#40;</span>AmazonS3Exception ex<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>ex.<span style="color: #0000FF;">StatusCode</span> == HttpStatusCode.<span style="color: #0000FF;">NotFound</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #0600FF;">false</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">throw</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb56374" style="display: none; color: red;"></div></div></div>

<p>It has gotten slightly more complex since then (I've added configurable headers and an option for forcing overwrite of the existing file) but the core logic remains the same.  It's a fairly naive implementation but my experience with the Amazon services has been good enough so far that I haven't encountered a lot of the exceptions that I hope to add handling for in the future.</p>

<h3>Adding Invalidation</h3>

<p>At this point we should be able to render our content directly to S3, but this doesn't get us all the way to where we need to be.  While hosting static content in S3 offers some advantages over hosting it locally and it <strong>can</strong> work as a CDN, using CloudFront to deliver your S3 content makes more sense if you really want to minimize latency.  To make this work we'll just need to add an invaliator to the mix.  A test for the core usage looks like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb48871'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb48871','cb58807'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb48871" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Invalidate<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var cloudfrontClient = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Mock&lt;AmazonCloudFront&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var distributionId = Guid.<span style="color: #0000FF;">NewGuid</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var bucket = Guid.<span style="color: #0000FF;">NewGuid</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var distribution = bucket + <span style="color: #808080;">&quot;.s3.amazonaws.com&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var key = Guid.<span style="color: #0000FF;">NewGuid</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var listDistributionsResponse = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ListDistributionsResponse<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; listDistributionsResponse.<span style="color: #0000FF;">Distribution</span>.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CloudFrontDistribution</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Id = distributionId,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; DistributionConfig = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CloudFrontDistributionConfig</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; S3Origin = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> S3Origin<span style="color: #000000;">&#40;</span>distribution, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; cloudfrontClient.<span style="color: #0000FF;">Setup</span><span style="color: #000000;">&#40;</span>cfc =&gt; cfc.<span style="color: #0000FF;">ListDistributions</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Returns</span><span style="color: #000000;">&#40;</span>listDistributionsResponse<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var invalidator = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CloudFrontInvalidator<span style="color: #000000;">&#40;</span>cloudfrontClient.<span style="color: #FF0000;">Object</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; invalidator.<span style="color: #0000FF;">InvalidateObject</span><span style="color: #000000;">&#40;</span>bucket, key<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; cloudfrontClient.<span style="color: #0000FF;">Verify</span><span style="color: #000000;">&#40;</span>cfc =&gt; cfc.<span style="color: #0000FF;">PostInvalidation</span><span style="color: #000000;">&#40;</span>It.<span style="color: #0000FF;">Is</span>&lt;PostInvalidationRequest&gt;<span style="color: #000000;">&#40;</span>pir =&gt; pir.<span style="color: #0000FF;">DistributionId</span> == distributionId</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &amp;&amp; pir.<span style="color: #0000FF;">InvalidationBatch</span>.<span style="color: #0000FF;">Paths</span>.<span style="color: #0000FF;">Count</span> == <span style="color: #FF0000;">1</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &amp;&amp; pir.<span style="color: #0000FF;">InvalidationBatch</span>.<span style="color: #0000FF;">Paths</span>.<span style="color: #0000FF;">First</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> == key<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb58807" style="display: none; color: red;"></div></div></div>

<p>The implementation is pretty straightforward, and will look very familiar to anyone who read my post regarding <a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api">copying buckets with the S3 API</a>.  There are only two changes, first that we only need to invalidate one object at a time, and second that we only want to query for the list of CloudFront distributions once.  Code for the CloudFront invalidator looks like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb13874'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb13874','cb18981'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb13874" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #FF0000;">class</span> CloudFrontInvalidator : IDisposable, IInvalidator</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">const</span> <span style="color: #FF0000;">string</span> amazonBucketUriSuffix = <span style="color: #808080;">&quot;.s3.amazonaws.com&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">const</span> <span style="color: #FF0000;">string</span> dateFormatWithMilliseconds = <span style="color: #808080;">&quot;yyyy-MM-dd hh:mm:ss.ff&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> AmazonCloudFront cloudFrontClient;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> CloudFrontInvalidator<span style="color: #000000;">&#40;</span>AmazonCloudFront cloudFrontClient<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">cloudFrontClient</span> = cloudFrontClient;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> InvalidateObject<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucket, <span style="color: #FF0000;">string</span> key<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var distId = GetDistributionIdFor<span style="color: #000000;">&#40;</span>bucket<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>!<span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrWhiteSpace</span><span style="color: #000000;">&#40;</span>distId<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var invalidationRequest = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> PostInvalidationRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithDistribtionId</span><span style="color: #000000;">&#40;</span>distId<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithInvalidationBatch</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> InvalidationBatch<span style="color: #000000;">&#40;</span>DateTime.<span style="color: #0000FF;">Now</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span>dateFormatWithMilliseconds<span style="color: #000000;">&#41;</span>, <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> List&lt;string&gt; <span style="color: #000000;">&#123;</span> key <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cloudFrontClient.<span style="color: #0000FF;">PostInvalidation</span><span style="color: #000000;">&#40;</span>invalidationRequest<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; Dictionary&lt;string, string&gt; distributionNameAndIds;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">string</span> GetDistributionIdFor<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucketName<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; distributionNameAndIds = distributionNameAndIds ??</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cloudFrontClient.<span style="color: #0000FF;">ListDistributions</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Distribution</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">ToDictionary</span><span style="color: #000000;">&#40;</span>cfd =&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cfd.<span style="color: #0000FF;">DistributionConfig</span>.<span style="color: #0000FF;">S3Origin</span>.<span style="color: #0000FF;">DNSName</span>.<span style="color: #0000FF;">Replace</span><span style="color: #000000;">&#40;</span>amazonBucketUriSuffix, <span style="color: #808080;">&quot;&quot;</span><span style="color: #000000;">&#41;</span>,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cfd =&gt; cfd.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #FF0000;">string</span> id = <span style="color: #0600FF;">null</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; distributionNameAndIds.<span style="color: #0000FF;">TryGetValue</span><span style="color: #000000;">&#40;</span>bucketName, <span style="color: #0600FF;">out</span> id<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> id;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Dispose<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; cloudFrontClient.<span style="color: #0000FF;">Dispose</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb18981" style="display: none; color: red;"></div></div></div>

<p>As an interesting aside, while I was working on this Amazon released <a href="http://aws.amazon.com/releasenotes/7875688065681094">support for querystring invalidation/versioning</a>, which is SquishIt's default behavior.  I had planned to add a release note telling people that they would need to use squishit's "hash in filename" option, but it seems like now there won't be any need <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p>

<h3>Neat, But How Do I Use This?</h3>

<p>It's nice that this all works on paper (and in unit tests) but how do we actually tie everything together?  One of the key design decisions was that the renderer is instantiated with pre-initialized CloudFront and S3 clients.  This way users aren't locked into a certain method of getting credentials or anything like that.  To use the custom renderer for only a particular bundle usage would be something like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb98085'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb98085','cb47889'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb98085" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">var s3client = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> AmazonS3Client<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;accessKey&quot;</span>, <span style="color: #808080;">&quot;secretKey&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">var renderer = S3Renderer.<span style="color: #0000FF;">Create</span><span style="color: #000000;">&#40;</span>s3client<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;bucket&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithDefaultKeyBuilder</span><span style="color: #000000;">&#40;</span>HttpContext.<span style="color: #0000FF;">Current</span>.<span style="color: #0000FF;">Request</span>.<span style="color: #0000FF;">PhysicalApplicationPath</span>,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HttpContext.<span style="color: #0000FF;">Current</span>.<span style="color: #0000FF;">Request</span>.<span style="color: #0000FF;">ApplicationPath</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithCannedAcl</span><span style="color: #000000;">&#40;</span>S3CannedACL.<span style="color: #0000FF;">PublicRead</span><span style="color: #000000;">&#41;</span> <span style="color: #0600FF;">as</span> IRenderer;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">Bundle.<span style="color: #0000FF;">JavaScript</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">WithReleaseFileRenderer</span><span style="color: #000000;">&#40;</span>renderer<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithOutputBaseHref</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://s3.amazonaws.com/bucket&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;file1.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;file2.js&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">Render</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;combined.js&quot;</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb47889" style="display: none; color: red;"></div></div></div>

<p>This is nice, but I think the global configuration is probably what people will be using more often.  As is common in ASP.net apps a lot of the setup magic for this happens in the app initialization.  So you'd add something like this to your Application_Start method (in Global.asax.cs):</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb69526'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb69526','cb32210'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb69526" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">var s3client = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> AmazonS3Client<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;accessKey&quot;</span>, <span style="color: #808080;">&quot;secretKey&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">var renderer = S3Renderer.<span style="color: #0000FF;">Create</span><span style="color: #000000;">&#40;</span>s3client<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;bucket&quot;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithDefaultKeyBuilder</span><span style="color: #000000;">&#40;</span>HttpContext.<span style="color: #0000FF;">Current</span>.<span style="color: #0000FF;">Request</span>.<span style="color: #0000FF;">PhysicalApplicationPath</span>,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HttpContext.<span style="color: #0000FF;">Current</span>.<span style="color: #0000FF;">Request</span>.<span style="color: #0000FF;">ApplicationPath</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">WithCannedAcl</span><span style="color: #000000;">&#40;</span>S3CannedACL.<span style="color: #0000FF;">PublicRead</span><span style="color: #000000;">&#41;</span> <span style="color: #0600FF;">as</span> IRenderer;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">Bundle.<span style="color: #0000FF;">ConfigureDefaults</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; .<span style="color: #0000FF;">UseReleaseRenderer</span><span style="color: #000000;">&#40;</span>renderer<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; .<span style="color: #0000FF;">UseDefaultOutputBaseHref</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://s3.amazonaws.com/bucket&quot;</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb32210" style="display: none; color: red;"></div></div></div>

<p>I tried to make this something that could be run via WebActivator, but had trouble finding a method to use that would have access to the HttpContext (needed to resolve application path and virtual directory) so for now it needs to be set up manually.  This may be for the best though, as it doesn't force any particular convention for access key / secret key retrieval.  It doesn't <strong>feel</strong> like a ton of setup code to me, hopefully others will agree.</p>

<h3>What's Next</h3>

<p>Now that SquishIt 0.8.7 has been released I can finally start planning to make this available <a href="http://nuget.org/packages/SquishIt.S3">on NuGet</a> as a standard package (currently in beta until I get a little more testing).  It can be installed like any other, but will require updating SquishIt if you're using a pre-0.8.7 version.  If you need to report any issues encountered while using the library, or feel like contributing some code, please do so <a href="https://github.com/AlexCuse/SquishIt.S3">on github</a>.  Oh, and if you just want to kick the tires on SquishIt without all this other nonsense, or try making it work with another CDN you can find the core library <a href="http://nuget.org/packages/SquishIt">on NuGet</a> as well.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/making-squishit-work-with-amazon">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/making-squishit-work-with-amazon#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1751</wfw:commentRss>
		</item>
				<item>
			<title>Copying Buckets With The Amazon S3 API</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api</link>
			<pubDate>Tue, 29 May 2012 14:43:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>			<guid isPermaLink="false">1734@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;One of the projects I have been working on is a system for managing content on our network of websites.  One of our requirements is that changes don&#039;t take effect immediately, but on a separate preview network where our customer can look to see that her changes show the way she expected before pushing them into the production environment.  Because of this requirement, we need to maintain 2 separate sets of product images (hosted on Amazon S3, with their CloudFront CDN used for the production sites).&lt;/p&gt;
&lt;p&gt;Allowing our content management system to save image changes to Amazon and render their locations in our app was trivial, but we found the push mechanism for moving changes into the live environment a bit challenging.  The work for moving database changes was already done when I started on the project, so most of the challenges I saw were in relation to copying the images.  What we really wanted was a true bucket copy (emptying the destination bucket altogether and replacing its content with that of the source bucket) - but the &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketOps.html&quot;&gt;REST API for S3&lt;/a&gt; does not support copy at the bucket level currently.&lt;/p&gt;
&lt;p&gt;When designing our solution we needed to optimize not only on performance and maintainability, but on cost.  The &lt;a href=&quot;http://aws.amazon.com/s3/pricing/&quot;&gt;pricing model for S3&lt;/a&gt; has charges associated with most request types so it&#039;s not efficient to simply copy all the items in the source bucket to the destination.  In addition, this wouldn&#039;t handle deleting objects that have been removed in the source bucket.  So the naive solution becomes a two step process of deleting all of a bucket&#039;s content, then copying over all the content from the other bucket.  In a scenario where the actual degree of change is likely to be minimal incurring this kind of cost is not a great option (and it&#039;s likely to perform poorly as well).  When you add the challenge of CDN cache invalidation and &lt;a href=&quot;http://aws.amazon.com/cloudfront/pricing/&quot;&gt;pricing for CloudFront&lt;/a&gt; to the equation this looks even less attractive.&lt;/p&gt;
&lt;p&gt;This post will go over the implementation we came up with for bucket copying.  Code is written in C# using the &lt;a href=&quot;http://aws.amazon.com/sdkfornet/&quot;&gt;AWS SDK for .net&lt;/a&gt;, but it is all possible using the REST API directly.  I will add that the AWS SDK is a very nice tool - typically I would choose to use &lt;a href=&quot;https://github.com/restsharp/restsharp&quot;&gt;RestSharp&lt;/a&gt; to go directly at the REST API, but Amazon has taken a lot of time to build a nice fluent syntax for building requests and some helper functions that make life easy enough to make it worth a look - it is far nicer than some of the other client libraries out there, in large part because it doesn&#039;t try to hide the fact that you&#039;re working with a set of webservices.  But I digress.  The process for copying a bucket is pretty straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check for destination bucket existence, create if needed&lt;/li&gt;
&lt;li&gt;List Objects in Both Buckets&lt;/li&gt;
&lt;li&gt;Identify keys for Insert, Update and Deletion (COPYs and DELETEs)&lt;/li&gt;
&lt;li&gt;Perform COPY / DELETE&lt;/li&gt;
&lt;li&gt;Invalidations (Updates only)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So here we go.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;Check for Bucket Existence&lt;/h3&gt;
&lt;p&gt;Checking for bucket existence is a piece of cake, thanks to a static helper function in the .net SDK that takes a bucket name and an s3 client.  I chose to hide this call behind an instance method so that I could mock it during unit tests, but this is not strictly necessary.  I think that behind the scenes this method simply issues a HEAD request on a bucket and returns false if a 404 is encountered.  As you&#039;d expect creating the bucket is done via a PUT request on the bucket resource.  Code is as follows:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb48659&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; CreateDestinationBucketIfNeeded&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!BucketExists&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; PutBucketRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;PutBucket&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;virtual&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; BucketExists&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; Amazon.&lt;span style=&quot;color: #0000FF;&quot;&gt;S3&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Util&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;AmazonS3Util&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;DoesS3BucketExist&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName, _s3Client&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb24761&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;List Objects in a Bucket&lt;/h3&gt;
&lt;p&gt;Listing objects in an S3 bucket is very easy.  You just need to issue a signed GET request to myBucket.s3.amazonaws.com.  The only gotcha is it only returns up to 1000 objects in a single response, so getting a complete list can take multiple requests.  It helps to know a few things when putting this together - first that objects are listed in alphabetical order, second that we can include a &quot;marker&quot; parameter in our request telling AWS what key to start with, and third that the response from this method includes an &quot;IsTruncated&quot; flag.  The C# code to list objects looks like this:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb48274&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;IEnumerable&amp;lt;S3Object&amp;gt; ObjectsFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var result = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; List&amp;lt;S3Object&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var response = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ListObjectsResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ListObjectsRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;response.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTruncated&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; request.&lt;span style=&quot;color: #0000FF;&quot;&gt;Marker&lt;/span&gt; = response.&lt;span style=&quot;color: #0000FF;&quot;&gt;NextMarker&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; response = _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;ListObjects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; result.&lt;span style=&quot;color: #0000FF;&quot;&gt;AddRange&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;response.&lt;span style=&quot;color: #0000FF;&quot;&gt;S3Objects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;response.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTruncated&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; response.&lt;span style=&quot;color: #0000FF;&quot;&gt;Dispose&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; result;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb74264&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;
&lt;p&gt;Identify Inserts, Updates and Deletes&lt;/p&gt;
&lt;/h3&gt;
&lt;p&gt;Once we have the contents of both buckets, we need to determine which objects need to be inserted to, updated in, and deleted from the destination bucket.  For deleted objects we will be issuing DELETE requests, and for Inserts and Updates we will be copying objects across using a special PUT request.  The DELETE requests can be batched, supporting up to 1000 deletions in a single request, while the PUT requests need to be issued on an object by object basis.&lt;/p&gt;
&lt;p&gt;These subsets of can be identified by comparing the objects in source and destination bucket.  Deletes will include all objects in the destination that aren&#039;t in the source.  Updates will include all objects present in both buckets that have changed (this can be determined by comparing the ETags on the objects).  Inserts will of course contain all objects that exist in the source but not the destination.&lt;/p&gt;
&lt;p&gt;C# helps us out a bit here, as LINQ makes it easy to do these comparisons without having to do any nasty looping.  For the inserts and deletes we need to spoof an outer join which LINQ doesn&#039;t really have a great API for, but I still find it helpful to be able to think about these operations in set-based terms instead of in terms of the tedious iterative comparison that actually gets done behind all the LINQ magic.&lt;/p&gt;
&lt;p&gt;Code to identify these sets of objects can be found here:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb7637&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;IEnumerable&amp;lt;S3Object&amp;gt; ObjectsToUpdate&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IEnumerable&amp;lt;S3Object&amp;gt; sourceObjects, IEnumerable&amp;lt;S3Object&amp;gt; destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; from src &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; sourceObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;join dest &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; destinationObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;on src.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; equals dest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;where src.&lt;span style=&quot;color: #0000FF;&quot;&gt;Size&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt; &amp;amp;&amp;amp; src.&lt;span style=&quot;color: #0000FF;&quot;&gt;ETag&lt;/span&gt; != dest.&lt;span style=&quot;color: #0000FF;&quot;&gt;ETag&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;select src;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;IEnumerable&amp;lt;S3Object&amp;gt; ObjectsToInsert&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IEnumerable&amp;lt;S3Object&amp;gt; sourceObjects, IEnumerable&amp;lt;S3Object&amp;gt; destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; from src &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; sourceObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;join dest &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; destinationObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;on src.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; equals dest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; into joinedObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;from coalescedDest &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; joinedObjects.&lt;span style=&quot;color: #0000FF;&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;where src.&lt;span style=&quot;color: #0000FF;&quot;&gt;Size&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt; &amp;amp;&amp;amp; coalescedDest == &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;select src;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;IEnumerable&amp;lt;S3Object&amp;gt; ObjectsToDelete&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IEnumerable&amp;lt;S3Object&amp;gt; sourceObjects, IEnumerable&amp;lt;S3Object&amp;gt; destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; from dest &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; destinationObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;join src &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; sourceObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;on dest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; equals src.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt; into joinedObjects&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;from coalescedSrc &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; joinedObjects.&lt;span style=&quot;color: #0000FF;&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;where dest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Size&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt; &amp;amp;&amp;amp; coalescedSrc == &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;select dest;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb61337&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;Perform Insert/Delete&lt;/h3&gt;
&lt;p&gt;Inserts and deletes are the easy part.  We just need to process the list of objects and issue the appropriate requests.  There are two important optimizations here - we can batch deletes, and we can parallelize copies.  Batching deletes will help to minimize network chatter, and parallelizing copies will help to force as much network chatter as possible into a shorter amount of time &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;  I was torn on whether or not to paralellize the copies, but decided that in essence we are performing a ton of blocking I/O requests &lt;em&gt;&lt;strong&gt;with another computer doing all the work&lt;/strong&gt;&lt;/em&gt;.  If you have real concerns about flooding your network connection you may not want to make this optimization, or do it in a way that allows you to dial down the amount of concurrency but after testing with the calls made in parallel I found a copy of a bucket with ~500 objects finished in about 25% of the time on my dual core laptop, and I was sold.&lt;/p&gt;

&lt;p&gt;The inserts are the easiest part:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb97616&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; CopyObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IEnumerable&amp;lt;S3Object&amp;gt; items, Func&amp;lt;S3Object, CopyObjectRequest&amp;gt; requestBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var exceptions = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ConcurrentQueue&amp;lt;Exception&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Parallel.&lt;span style=&quot;color: #0600FF;&quot;&gt;ForEach&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;items, obj =&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;try&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var captured = obj;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = requestBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;obj&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;CopyObject&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Exception ex&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exceptions.&lt;span style=&quot;color: #0000FF;&quot;&gt;Enqueue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ex&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;exceptions.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;throw&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; AggregateException&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;exceptions&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb90275&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Deletes were pretty simple as well, only difference being the batching of requests:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb85831&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;var toDelete = ObjectsToDelete&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceObjects, destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToList&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var batch = toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Take&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; DeleteObjectsRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;batch.&lt;span style=&quot;color: #0000FF;&quot;&gt;Select&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;k =&amp;gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; KeyVersion&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;k.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToArray&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;DeleteObjects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;RemoveRange&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;, toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt; ? &lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt; : toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb19242&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Perform Updates&lt;/h3&gt;
&lt;p&gt;This step is not really anything special but it is different enough for me to exclude it from it&#039;s counterparts in step 3.  We are basically doing the same copy operation for each object that we did for the inserts, but we also need to worry about cache invalidation on the CloudFront CDN.  The invalidation can be batched, so it makes sense to build the list of keys updated while we do the copy, then send a single invalidation request.&lt;/p&gt;
&lt;p&gt;So we can change the code for copy to something like this, taking an optional parameter containing a function to add the object&#039;s key to a list of keys to be invalidated:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb76628&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; CopyObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IEnumerable&amp;lt;S3Object&amp;gt; items, Func&amp;lt;S3Object, CopyObjectRequest&amp;gt; requestBuilder, Action&amp;lt;string&amp;gt; addToInvalidationList = &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var exceptions = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ConcurrentQueue&amp;lt;Exception&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Parallel.&lt;span style=&quot;color: #0600FF;&quot;&gt;ForEach&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;items, obj =&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;try&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var captured = obj;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = requestBuilder&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;obj&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;CopyObject&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;addToInvalidationList != &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; addToInvalidationList&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;captured.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Exception ex&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exceptions.&lt;span style=&quot;color: #0000FF;&quot;&gt;Enqueue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ex&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;exceptions.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;throw&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; AggregateException&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;exceptions&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb1343&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I&#039;ll concede that this is not the simplest possible thing - it would probably be easier to call the old copy code and then pass the keys from the updated objects collection, but I do like that it captures the keys of the objects that are actually copied.  If we changed the approach to do something like what is happening with the delete operation that trims the collection while processing, the keys collection we pass for invalidation would end up empty.  I could also just have too much functional programming on the brain I guess &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif&quot; title=&quot;;)&quot; alt=&quot;;)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once we have the list of keys, performing the actual invalidation is relatively simple.  We do need to generate a unique &quot;caller reference&quot; that amazon uses to ensure that duplicate requests aren&#039;t processed (remember, each object invalidated triggers some kind of action on potentially hundreds of servers, so this is not a cheap operation for you OR for Amazon).  The hardest part is probably identifying whether there is a CloudFront distribution to worry about in the first place.  The key to finding whether there is a distribution attached to a bucket or not relies on the knowledge that for an s3 origin, the DNSName property will be &quot;bucketname.s3.amazonaws.com&quot; - so by stripping out &quot;.s3.amazonaws.com&quot; from our available distributions&#039; DNS names we can find if one matches our bucket.&lt;/p&gt;
&lt;p&gt;The code for invalidation looks like this:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb8681&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;const&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; dateFormatWithMilliseconds = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;yyyy-MM-dd hh:mm:ss.ff&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; InvalidateObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; destinationBucket, List&amp;lt;string&amp;gt; keysToInvalidate&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;keysToInvalidate.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var distId = GetDistributionIdFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;distId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var invalidationRequest = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; PostInvalidationRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDistribtionId&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;distId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithInvalidationBatch&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; InvalidationBatch&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;DateTime.&lt;span style=&quot;color: #0000FF;&quot;&gt;Now&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;dateFormatWithMilliseconds&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;keysToInvalidate&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _cloudFrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;PostInvalidation&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;invalidationRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;const&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; amazonBucketUriSuffix = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.s3.amazonaws.com&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; GetDistributionIdFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; bucketName&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var distributionNameAndIds =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _cloudFrontClient.&lt;span style=&quot;color: #0000FF;&quot;&gt;ListDistributions&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;Distribution&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;ToDictionary&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;cfd =&amp;gt; cfd.&lt;span style=&quot;color: #0000FF;&quot;&gt;DistributionConfig&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;S3Origin&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;DNSName&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Replace&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;amazonBucketUriSuffix, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, cfd =&amp;gt; cfd.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; id = &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; distributionNameAndIds.&lt;span style=&quot;color: #0000FF;&quot;&gt;TryGetValue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;bucketName, &lt;span style=&quot;color: #0600FF;&quot;&gt;out&lt;/span&gt; id&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; id;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb94991&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Tying it All Together&lt;/h3&gt;
&lt;p&gt;OK so we have all these methods to facilitate copying buckets but how do we actually do it?  I&#039;m glad you asked.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb91887&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Copy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; sourceBucket, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; CreateDestinationBucketIfNeeded&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var sourceObjects = ObjectsFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var destinationObjects = ObjectsFor&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var toDelete = ObjectsToDelete&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceObjects, destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToList&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var batch = toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Take&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var request = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; DeleteObjectsRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithBucketName&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;batch.&lt;span style=&quot;color: #0000FF;&quot;&gt;Select&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;k =&amp;gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; KeyVersion&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;k.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToArray&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _s3Client.&lt;span style=&quot;color: #0000FF;&quot;&gt;DeleteObjects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;request&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;RemoveRange&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;0&lt;/span&gt;, toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt; &amp;gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt; ? &lt;span style=&quot;color: #FF0000;&quot;&gt;1000&lt;/span&gt; : toDelete.&lt;span style=&quot;color: #0000FF;&quot;&gt;Count&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var buildCopyRequest = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Func&amp;lt;S3Object, CopyObjectRequest&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3obj =&amp;gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CopyObjectRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithSourceBucket&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDestinationBucket&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithSourceKey&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3obj.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithDestinationKey&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;s3obj.&lt;span style=&quot;color: #0000FF;&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .&lt;span style=&quot;color: #0000FF;&quot;&gt;WithCannedACL&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;S3CannedACL.&lt;span style=&quot;color: #0000FF;&quot;&gt;PublicRead&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; CopyObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ObjectsToInsert&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceObjects, destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, buildCopyRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var keysToInvalidate = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; ConcurrentBag&amp;lt;string&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; CopyObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ObjectsToUpdate&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sourceObjects, destinationObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, buildCopyRequest, keysToInvalidate.&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; InvalidateObjects&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;destinationBucket, keysToInvalidate.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToList&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb24200&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note that the delete happens inline instead of in a separate method as with the copies.  This is because it wasn&#039;t reusable, and because the act of deleting the items changes the collection.  As a result it seemed cleaner to just do it inline.&lt;/p&gt; 
&lt;p&gt;This is really all there is to it.  Hopefully copying buckets in this manner is something that finds its way into the REST API for S3 at some point, but in the meantime this process seems to be working pretty well.  It is not the prettiest code, but it runs reasonably fast for our smallish buckets, and I think it is doing a good job minimizing what our customer has to pay for the service each month.  It was interesting working on this problem, because it forced thinking about the problem in terms of Amazon&#039;s pricing structure (though because Amazon doesn&#039;t charge for data transfer within s3 regions it really becomes the same as thinking in terms of minimizing http requests).  This kind of thing isn&#039;t always a driver of implementation so it was a nice mental exercise.  Now we just have to hope they never change their pricing structure &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif&quot; title=&quot;;)&quot; alt=&quot;;)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>One of the projects I have been working on is a system for managing content on our network of websites.  One of our requirements is that changes don't take effect immediately, but on a separate preview network where our customer can look to see that her changes show the way she expected before pushing them into the production environment.  Because of this requirement, we need to maintain 2 separate sets of product images (hosted on Amazon S3, with their CloudFront CDN used for the production sites).</p>
<p>Allowing our content management system to save image changes to Amazon and render their locations in our app was trivial, but we found the push mechanism for moving changes into the live environment a bit challenging.  The work for moving database changes was already done when I started on the project, so most of the challenges I saw were in relation to copying the images.  What we really wanted was a true bucket copy (emptying the destination bucket altogether and replacing its content with that of the source bucket) - but the <a href="http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketOps.html">REST API for S3</a> does not support copy at the bucket level currently.</p>
<p>When designing our solution we needed to optimize not only on performance and maintainability, but on cost.  The <a href="http://aws.amazon.com/s3/pricing/">pricing model for S3</a> has charges associated with most request types so it's not efficient to simply copy all the items in the source bucket to the destination.  In addition, this wouldn't handle deleting objects that have been removed in the source bucket.  So the naive solution becomes a two step process of deleting all of a bucket's content, then copying over all the content from the other bucket.  In a scenario where the actual degree of change is likely to be minimal incurring this kind of cost is not a great option (and it's likely to perform poorly as well).  When you add the challenge of CDN cache invalidation and <a href="http://aws.amazon.com/cloudfront/pricing/">pricing for CloudFront</a> to the equation this looks even less attractive.</p>
<p>This post will go over the implementation we came up with for bucket copying.  Code is written in C# using the <a href="http://aws.amazon.com/sdkfornet/">AWS SDK for .net</a>, but it is all possible using the REST API directly.  I will add that the AWS SDK is a very nice tool - typically I would choose to use <a href="https://github.com/restsharp/restsharp">RestSharp</a> to go directly at the REST API, but Amazon has taken a lot of time to build a nice fluent syntax for building requests and some helper functions that make life easy enough to make it worth a look - it is far nicer than some of the other client libraries out there, in large part because it doesn't try to hide the fact that you're working with a set of webservices.  But I digress.  The process for copying a bucket is pretty straightforward:</p>
<ol>
<li>Check for destination bucket existence, create if needed</li>
<li>List Objects in Both Buckets</li>
<li>Identify keys for Insert, Update and Deletion (COPYs and DELETEs)</li>
<li>Perform COPY / DELETE</li>
<li>Invalidations (Updates only)</li>
</ol>
<p>So here we go.</p>
<p> </p>
<h3>Check for Bucket Existence</h3>
<p>Checking for bucket existence is a piece of cake, thanks to a static helper function in the .net SDK that takes a bucket name and an s3 client.  I chose to hide this call behind an instance method so that I could mock it during unit tests, but this is not strictly necessary.  I think that behind the scenes this method simply issues a HEAD request on a bucket and returns false if a 404 is encountered.  As you'd expect creating the bucket is done via a PUT request on the bucket resource.  Code is as follows:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb78356'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb78356','cb32761'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb78356" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">void</span> CreateDestinationBucketIfNeeded<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucketName<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>!BucketExists<span style="color: #000000;">&#40;</span>bucketName<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> PutBucketRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>bucketName<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; _s3Client.<span style="color: #0000FF;">PutBucket</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">virtual</span> <span style="color: #FF0000;">bool</span> BucketExists<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucketName<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> Amazon.<span style="color: #0000FF;">S3</span>.<span style="color: #0000FF;">Util</span>.<span style="color: #0000FF;">AmazonS3Util</span>.<span style="color: #0000FF;">DoesS3BucketExist</span><span style="color: #000000;">&#40;</span>bucketName, _s3Client<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb32761" style="display: none; color: red;"></div></div></div>
<h3>List Objects in a Bucket</h3>
<p>Listing objects in an S3 bucket is very easy.  You just need to issue a signed GET request to myBucket.s3.amazonaws.com.  The only gotcha is it only returns up to 1000 objects in a single response, so getting a complete list can take multiple requests.  It helps to know a few things when putting this together - first that objects are listed in alphabetical order, second that we can include a "marker" parameter in our request telling AWS what key to start with, and third that the response from this method includes an "IsTruncated" flag.  The C# code to list objects looks like this:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb16539'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb16539','cb60076'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb16539" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">IEnumerable&lt;S3Object&gt; ObjectsFor<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucketName<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var result = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> List&lt;S3Object&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var response = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ListObjectsResponse<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">do</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ListObjectsRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>bucketName<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>response.<span style="color: #0000FF;">IsTruncated</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; request.<span style="color: #0000FF;">Marker</span> = response.<span style="color: #0000FF;">NextMarker</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; response = _s3Client.<span style="color: #0000FF;">ListObjects</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; result.<span style="color: #0000FF;">AddRange</span><span style="color: #000000;">&#40;</span>response.<span style="color: #0000FF;">S3Objects</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span> <span style="color: #0600FF;">while</span><span style="color: #000000;">&#40;</span>response.<span style="color: #0000FF;">IsTruncated</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; </li><li style="" class="li1">&nbsp; &nbsp; response.<span style="color: #0000FF;">Dispose</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> result;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb60076" style="display: none; color: red;"></div></div></div>

<h3>
<p>Identify Inserts, Updates and Deletes</p>
</h3>
<p>Once we have the contents of both buckets, we need to determine which objects need to be inserted to, updated in, and deleted from the destination bucket.  For deleted objects we will be issuing DELETE requests, and for Inserts and Updates we will be copying objects across using a special PUT request.  The DELETE requests can be batched, supporting up to 1000 deletions in a single request, while the PUT requests need to be issued on an object by object basis.</p>
<p>These subsets of can be identified by comparing the objects in source and destination bucket.  Deletes will include all objects in the destination that aren't in the source.  Updates will include all objects present in both buckets that have changed (this can be determined by comparing the ETags on the objects).  Inserts will of course contain all objects that exist in the source but not the destination.</p>
<p>C# helps us out a bit here, as LINQ makes it easy to do these comparisons without having to do any nasty looping.  For the inserts and deletes we need to spoof an outer join which LINQ doesn't really have a great API for, but I still find it helpful to be able to think about these operations in set-based terms instead of in terms of the tedious iterative comparison that actually gets done behind all the LINQ magic.</p>
<p>Code to identify these sets of objects can be found here:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb52015'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb52015','cb14176'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb52015" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">IEnumerable&lt;S3Object&gt; ObjectsToUpdate<span style="color: #000000;">&#40;</span>IEnumerable&lt;S3Object&gt; sourceObjects, IEnumerable&lt;S3Object&gt; destinationObjects<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> from src <span style="color: #0600FF;">in</span> sourceObjects</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;join dest <span style="color: #0600FF;">in</span> destinationObjects</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;on src.<span style="color: #0000FF;">Key</span> equals dest.<span style="color: #0000FF;">Key</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;where src.<span style="color: #0000FF;">Size</span> &gt; <span style="color: #FF0000;">0</span> &amp;&amp; src.<span style="color: #0000FF;">ETag</span> != dest.<span style="color: #0000FF;">ETag</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;select src;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">IEnumerable&lt;S3Object&gt; ObjectsToInsert<span style="color: #000000;">&#40;</span>IEnumerable&lt;S3Object&gt; sourceObjects, IEnumerable&lt;S3Object&gt; destinationObjects<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> from src <span style="color: #0600FF;">in</span> sourceObjects</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;join dest <span style="color: #0600FF;">in</span> destinationObjects</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;on src.<span style="color: #0000FF;">Key</span> equals dest.<span style="color: #0000FF;">Key</span> into joinedObjects</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;from coalescedDest <span style="color: #0600FF;">in</span> joinedObjects.<span style="color: #0000FF;">DefaultIfEmpty</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;where src.<span style="color: #0000FF;">Size</span> &gt; <span style="color: #FF0000;">0</span> &amp;&amp; coalescedDest == <span style="color: #0600FF;">null</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;select src;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">IEnumerable&lt;S3Object&gt; ObjectsToDelete<span style="color: #000000;">&#40;</span>IEnumerable&lt;S3Object&gt; sourceObjects, IEnumerable&lt;S3Object&gt; destinationObjects<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> from dest <span style="color: #0600FF;">in</span> destinationObjects</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;join src <span style="color: #0600FF;">in</span> sourceObjects</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;on dest.<span style="color: #0000FF;">Key</span> equals src.<span style="color: #0000FF;">Key</span> into joinedObjects</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;from coalescedSrc <span style="color: #0600FF;">in</span> joinedObjects.<span style="color: #0000FF;">DefaultIfEmpty</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;where dest.<span style="color: #0000FF;">Size</span> &gt; <span style="color: #FF0000;">0</span> &amp;&amp; coalescedSrc == <span style="color: #0600FF;">null</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;select dest;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb14176" style="display: none; color: red;"></div></div></div>

<h3>Perform Insert/Delete</h3>
<p>Inserts and deletes are the easy part.  We just need to process the list of objects and issue the appropriate requests.  There are two important optimizations here - we can batch deletes, and we can parallelize copies.  Batching deletes will help to minimize network chatter, and parallelizing copies will help to force as much network chatter as possible into a shorter amount of time <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" />  I was torn on whether or not to paralellize the copies, but decided that in essence we are performing a ton of blocking I/O requests <em><strong>with another computer doing all the work</strong></em>.  If you have real concerns about flooding your network connection you may not want to make this optimization, or do it in a way that allows you to dial down the amount of concurrency but after testing with the calls made in parallel I found a copy of a bucket with ~500 objects finished in about 25% of the time on my dual core laptop, and I was sold.</p>

<p>The inserts are the easiest part:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb53862'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb53862','cb28086'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb53862" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">void</span> CopyObjects<span style="color: #000000;">&#40;</span>IEnumerable&lt;S3Object&gt; items, Func&lt;S3Object, CopyObjectRequest&gt; requestBuilder<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var exceptions = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ConcurrentQueue&lt;Exception&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; Parallel.<span style="color: #0600FF;">ForEach</span><span style="color: #000000;">&#40;</span>items, obj =&gt;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">try</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var captured = obj;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var request = requestBuilder<span style="color: #000000;">&#40;</span>obj<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _s3Client.<span style="color: #0000FF;">CopyObject</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">catch</span><span style="color: #000000;">&#40;</span>Exception ex<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exceptions.<span style="color: #0000FF;">Enqueue</span><span style="color: #000000;">&#40;</span>ex<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>exceptions.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span> <span style="color: #0600FF;">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> AggregateException<span style="color: #000000;">&#40;</span>exceptions<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb28086" style="display: none; color: red;"></div></div></div>

<p>Deletes were pretty simple as well, only difference being the batching of requests:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb30839'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb30839','cb41555'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb30839" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">var toDelete = ObjectsToDelete<span style="color: #000000;">&#40;</span>sourceObjects, destinationObjects<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToList</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">while</span><span style="color: #000000;">&#40;</span>toDelete.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var batch = toDelete.<span style="color: #0000FF;">Take</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">1000</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> DeleteObjectsRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithKeys</span><span style="color: #000000;">&#40;</span>batch.<span style="color: #0000FF;">Select</span><span style="color: #000000;">&#40;</span>k =&gt; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> KeyVersion<span style="color: #000000;">&#40;</span>k.<span style="color: #0000FF;">Key</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToArray</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; _s3Client.<span style="color: #0000FF;">DeleteObjects</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; toDelete.<span style="color: #0000FF;">RemoveRange</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">0</span>, toDelete.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">1000</span> ? <span style="color: #FF0000;">1000</span> : toDelete.<span style="color: #0000FF;">Count</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb41555" style="display: none; color: red;"></div></div></div>
<h3>Perform Updates</h3>
<p>This step is not really anything special but it is different enough for me to exclude it from it's counterparts in step 3.  We are basically doing the same copy operation for each object that we did for the inserts, but we also need to worry about cache invalidation on the CloudFront CDN.  The invalidation can be batched, so it makes sense to build the list of keys updated while we do the copy, then send a single invalidation request.</p>
<p>So we can change the code for copy to something like this, taking an optional parameter containing a function to add the object's key to a list of keys to be invalidated:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb82251'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb82251','cb3246'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb82251" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">void</span> CopyObjects<span style="color: #000000;">&#40;</span>IEnumerable&lt;S3Object&gt; items, Func&lt;S3Object, CopyObjectRequest&gt; requestBuilder, Action&lt;string&gt; addToInvalidationList = <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; var exceptions = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ConcurrentQueue&lt;Exception&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; Parallel.<span style="color: #0600FF;">ForEach</span><span style="color: #000000;">&#40;</span>items, obj =&gt;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">try</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var captured = obj;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var request = requestBuilder<span style="color: #000000;">&#40;</span>obj<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _s3Client.<span style="color: #0000FF;">CopyObject</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>addToInvalidationList != <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addToInvalidationList<span style="color: #000000;">&#40;</span>captured.<span style="color: #0000FF;">Key</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">catch</span><span style="color: #000000;">&#40;</span>Exception ex<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exceptions.<span style="color: #0000FF;">Enqueue</span><span style="color: #000000;">&#40;</span>ex<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>exceptions.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span> <span style="color: #0600FF;">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> AggregateException<span style="color: #000000;">&#40;</span>exceptions<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb3246" style="display: none; color: red;"></div></div></div>
<p>I'll concede that this is not the simplest possible thing - it would probably be easier to call the old copy code and then pass the keys from the updated objects collection, but I do like that it captures the keys of the objects that are actually copied.  If we changed the approach to do something like what is happening with the delete operation that trims the collection while processing, the keys collection we pass for invalidation would end up empty.  I could also just have too much functional programming on the brain I guess <img src="http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif" title=";)" alt=";)" class="middle" width="15" height="15" /></p>
<p>Once we have the list of keys, performing the actual invalidation is relatively simple.  We do need to generate a unique "caller reference" that amazon uses to ensure that duplicate requests aren't processed (remember, each object invalidated triggers some kind of action on potentially hundreds of servers, so this is not a cheap operation for you OR for Amazon).  The hardest part is probably identifying whether there is a CloudFront distribution to worry about in the first place.  The key to finding whether there is a distribution attached to a bucket or not relies on the knowledge that for an s3 origin, the DNSName property will be "bucketname.s3.amazonaws.com" - so by stripping out ".s3.amazonaws.com" from our available distributions' DNS names we can find if one matches our bucket.</p>
<p>The code for invalidation looks like this:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb63053'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb63053','cb87002'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb63053" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">const</span> <span style="color: #FF0000;">string</span> dateFormatWithMilliseconds = <span style="color: #808080;">&quot;yyyy-MM-dd hh:mm:ss.ff&quot;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">void</span> InvalidateObjects<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> destinationBucket, List&lt;string&gt; keysToInvalidate<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>keysToInvalidate.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var distId = GetDistributionIdFor<span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span><span style="color: #000000;">&#40;</span>!<span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrWhiteSpace</span><span style="color: #000000;">&#40;</span>distId<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var invalidationRequest = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> PostInvalidationRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithDistribtionId</span><span style="color: #000000;">&#40;</span>distId<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithInvalidationBatch</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> InvalidationBatch<span style="color: #000000;">&#40;</span>DateTime.<span style="color: #0000FF;">Now</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span>dateFormatWithMilliseconds<span style="color: #000000;">&#41;</span>,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;keysToInvalidate<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _cloudFrontClient.<span style="color: #0000FF;">PostInvalidation</span><span style="color: #000000;">&#40;</span>invalidationRequest<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #0600FF;">const</span> <span style="color: #FF0000;">string</span> amazonBucketUriSuffix = <span style="color: #808080;">&quot;.s3.amazonaws.com&quot;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #FF0000;">string</span> GetDistributionIdFor<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> bucketName<span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var distributionNameAndIds =</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; _cloudFrontClient.<span style="color: #0000FF;">ListDistributions</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">Distribution</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">ToDictionary</span><span style="color: #000000;">&#40;</span>cfd =&gt; cfd.<span style="color: #0000FF;">DistributionConfig</span>.<span style="color: #0000FF;">S3Origin</span>.<span style="color: #0000FF;">DNSName</span>.<span style="color: #0000FF;">Replace</span><span style="color: #000000;">&#40;</span>amazonBucketUriSuffix, <span style="color: #808080;">&quot;&quot;</span><span style="color: #000000;">&#41;</span>, cfd =&gt; cfd.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">string</span> id = <span style="color: #0600FF;">null</span>;</li><li style="" class="li2">&nbsp; &nbsp; distributionNameAndIds.<span style="color: #0000FF;">TryGetValue</span><span style="color: #000000;">&#40;</span>bucketName, <span style="color: #0600FF;">out</span> id<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">return</span> id;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb87002" style="display: none; color: red;"></div></div></div>
<h3>Tying it All Together</h3>
<p>OK so we have all these methods to facilitate copying buckets but how do we actually do it?  I'm glad you asked.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb52166'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb52166','cb61598'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb52166" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Copy<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> sourceBucket, <span style="color: #FF0000;">string</span> destinationBucket<span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; CreateDestinationBucketIfNeeded<span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var sourceObjects = ObjectsFor<span style="color: #000000;">&#40;</span>sourceBucket<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; var destinationObjects = ObjectsFor<span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var toDelete = ObjectsToDelete<span style="color: #000000;">&#40;</span>sourceObjects, destinationObjects<span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToList</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">while</span><span style="color: #000000;">&#40;</span>toDelete.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">0</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var batch = toDelete.<span style="color: #0000FF;">Take</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">1000</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var request = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> DeleteObjectsRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithBucketName</span><span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithKeys</span><span style="color: #000000;">&#40;</span>batch.<span style="color: #0000FF;">Select</span><span style="color: #000000;">&#40;</span>k =&gt; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> KeyVersion<span style="color: #000000;">&#40;</span>k.<span style="color: #0000FF;">Key</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">ToArray</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; _s3Client.<span style="color: #0000FF;">DeleteObjects</span><span style="color: #000000;">&#40;</span>request<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; toDelete.<span style="color: #0000FF;">RemoveRange</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">0</span>, toDelete.<span style="color: #0000FF;">Count</span> &gt; <span style="color: #FF0000;">1000</span> ? <span style="color: #FF0000;">1000</span> : toDelete.<span style="color: #0000FF;">Count</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var buildCopyRequest = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Func&lt;S3Object, CopyObjectRequest&gt;<span style="color: #000000;">&#40;</span>s3obj =&gt; <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CopyObjectRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithSourceBucket</span><span style="color: #000000;">&#40;</span>sourceBucket<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithDestinationBucket</span><span style="color: #000000;">&#40;</span>destinationBucket<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithSourceKey</span><span style="color: #000000;">&#40;</span>s3obj.<span style="color: #0000FF;">Key</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithDestinationKey</span><span style="color: #000000;">&#40;</span>s3obj.<span style="color: #0000FF;">Key</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #0000FF;">WithCannedACL</span><span style="color: #000000;">&#40;</span>S3CannedACL.<span style="color: #0000FF;">PublicRead</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; CopyObjects<span style="color: #000000;">&#40;</span>ObjectsToInsert<span style="color: #000000;">&#40;</span>sourceObjects, destinationObjects<span style="color: #000000;">&#41;</span>, buildCopyRequest<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; var keysToInvalidate = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> ConcurrentBag&lt;string&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; CopyObjects<span style="color: #000000;">&#40;</span>ObjectsToUpdate<span style="color: #000000;">&#40;</span>sourceObjects, destinationObjects<span style="color: #000000;">&#41;</span>, buildCopyRequest, keysToInvalidate.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; InvalidateObjects<span style="color: #000000;">&#40;</span>destinationBucket, keysToInvalidate.<span style="color: #0000FF;">ToList</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb61598" style="display: none; color: red;"></div></div></div>
<p>Note that the delete happens inline instead of in a separate method as with the copies.  This is because it wasn't reusable, and because the act of deleting the items changes the collection.  As a result it seemed cleaner to just do it inline.</p> 
<p>This is really all there is to it.  Hopefully copying buckets in this manner is something that finds its way into the REST API for S3 at some point, but in the meantime this process seems to be working pretty well.  It is not the prettiest code, but it runs reasonably fast for our smallish buckets, and I think it is doing a good job minimizing what our customer has to pay for the service each month.  It was interesting working on this problem, because it forced thinking about the problem in terms of Amazon's pricing structure (though because Amazon doesn't charge for data transfer within s3 regions it really becomes the same as thinking in terms of minimizing http requests).  This kind of thing isn't always a driver of implementation so it was a nice mental exercise.  Now we just have to hope they never change their pricing structure <img src="http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif" title=";)" alt=";)" class="middle" width="15" height="15" /></p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/copying-buckets-with-the-amazon-s3-api#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1734</wfw:commentRss>
		</item>
				<item>
			<title>Handling Unauthenticated AJAX Requests</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/handling-unauthenticated-ajax-requests</link>
			<pubDate>Wed, 15 Feb 2012 11:12:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="alt">UI Development</category>
<category domain="main">ASP.NET</category>
<category domain="alt">AJAX</category>			<guid isPermaLink="false">1624@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;A common pattern that I use in creating ajaxy applications is to return a small HTML fragment from the request, and then inject this fragment into the DOM in the callback executed after a successful request.  This tends to be a bit simpler than returning JSON and picking it apart to update the page, but it has one major problem, at least when using normal forms authentication.  If the user gets logged out (either by logging out from another tab or an expiring session), the AJAX request gets redirected to the login page, which is then returned and inserted into the page.  You can see how hideous this can become in the picture below.&lt;/p&gt;

&lt;div class=&quot;image_block&quot;&gt;&lt;a href=&quot;http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/bad-logon.PNG?mtime=1328983889&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/bad-logon.PNG?mtime=1328983889&quot; width=&quot;941&quot; height=&quot;644&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;In this case, when you sign up for a task it is supposed to return the updated task, and use this task to replace the notecard into the DOM on the right hand side.  It can actually get uglier, as we support signing up for multiple tasks at a time.  However, if the session expires we get a big, ugly login page displayed in the midst of all our pretty notecards.  What I&#039;d like to find is a way to retain the convenience of using forms authentication, but handle scenarios like this more gracefully.&lt;/p&gt;

&lt;p&gt;Adding a piece of metadata to the login page seemed like a good way to get this done without making things any harder on the user.  I initially wanted to get the login page classified as an error, so that redirection could be accomplished on the client side using the error callback available when using jQuery for AJAX requests.  This would be nice, but in jQuery 1.5 and above a &quot;statusCode&quot; callback has been added that is even nicer.  You can use the callback like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb38986&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$.ajax({&lt;br /&gt;&amp;nbsp; statusCode: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; 404: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; alert(&#039;page not found&#039;);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb54242&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The ease with which this allows you to define behavior for different status codes is fantastic.  As I set off down this path, the most obvious choice seemed to be adding a 401 (unauthorized) status code to the login page, but this got us into a weird redirect loop because forms authentication redirects all 401&#039;s to the login page - causing you lose the return URL, and redirect users back to the login page once they are authenticated.  Not exactly a paragon of usability.&lt;/p&gt;

&lt;p&gt;Having found this out the hard way, I decided a custom status code might be better.  It&#039;s easy enough to add the custom status code to the login page with a single line of C#:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb28184&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Response.&lt;span style=&quot;color: #0000FF;&quot;&gt;StatusCode&lt;/span&gt; = &lt;span style=&quot;color: #FF0000;&quot;&gt;999&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb11129&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then make our requests using something along these lines:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb51075&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$.ajax({&lt;br /&gt;&amp;nbsp; &amp;nbsp; type: &#039;POST&#039;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; url: &#039;/Task/SignUp&#039;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; data: &#039;projectId=&#039; + pid + &#039;&amp;amp;storyId=&#039; + sid + &#039;&amp;amp;id=&#039; + id + &#039;&amp;amp;initials=&#039; + initials,&lt;br /&gt;&amp;nbsp; &amp;nbsp; success: function (html) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $(&#039;#&#039; + id).replaceWith(html);&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; statusCode: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 999: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; location.href = &#039;/Account/LogOn?returnUrl=&#039; + location.href;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb984&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works well enough, at least from cassini.  When deployed to an IIS server however, we noticed that the 999 status code was getting picked up by the error handling modules, and of course we did not have an error page set up for that code.  To get around that we had to add the following to the code to display our login view:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb82242&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Response.&lt;span style=&quot;color: #0000FF;&quot;&gt;TrySkipIisCustomErrors&lt;/span&gt; = &lt;span style=&quot;color: #0600FF;&quot;&gt;true&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb85001&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That&#039;s kind of nasty, but it seems to allow us to accomplish our goal.  I think I can stomach it on this one page in the name of improving the user&#039;s experience on the site.  &lt;/p&gt;

&lt;p&gt;There&#039;s one more thing we can do to make our lives easier.  There is no special behavior in our statusCode handler, so it would be nice define it only once.  Luckily, the folks at jQuery are a step ahead of us.  We can define our statusCode handler using the ajaxSetup method in our master page:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb95801&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$.ajaxSetup({&lt;br /&gt;&amp;nbsp; &amp;nbsp; statusCode: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 999: function () {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; location.href = &#039;/Account/LogOn?returnUrl=&#039; + location.href;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb42648&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that everything is set up, we are properly redirected to the login page:&lt;/p&gt;

&lt;div class=&quot;image_block&quot;&gt;&lt;a href=&quot;http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/good-logon.PNG?mtime=1328987447&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/good-logon.PNG?mtime=1328987447&quot; width=&quot;557&quot; height=&quot;435&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;I&#039;m not sure this is the best solution to our problem, but it is certainly a solution.  It allows us to keep leveraging ASP.net&#039;s built in error handling and authentication (I know they aren&#039;t perfect, but they are good enough for us in this scenario) while making the user&#039;s life a bit easier in the event something goes wrong.  In this case, my team is the primary user of the application so it makes our lives easier &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Complete source code for the application in question is available on &lt;a href=&quot;https://github.com/jawsthegame/PivotalExtension&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/handling-unauthenticated-ajax-requests&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>A common pattern that I use in creating ajaxy applications is to return a small HTML fragment from the request, and then inject this fragment into the DOM in the callback executed after a successful request.  This tends to be a bit simpler than returning JSON and picking it apart to update the page, but it has one major problem, at least when using normal forms authentication.  If the user gets logged out (either by logging out from another tab or an expiring session), the AJAX request gets redirected to the login page, which is then returned and inserted into the page.  You can see how hideous this can become in the picture below.</p>

<div class="image_block"><a href="http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/bad-logon.PNG?mtime=1328983889"><img alt="" src="http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/bad-logon.PNG?mtime=1328983889" width="941" height="644" /></a></div>

<p>In this case, when you sign up for a task it is supposed to return the updated task, and use this task to replace the notecard into the DOM on the right hand side.  It can actually get uglier, as we support signing up for multiple tasks at a time.  However, if the session expires we get a big, ugly login page displayed in the midst of all our pretty notecards.  What I'd like to find is a way to retain the convenience of using forms authentication, but handle scenarios like this more gracefully.</p>

<p>Adding a piece of metadata to the login page seemed like a good way to get this done without making things any harder on the user.  I initially wanted to get the login page classified as an error, so that redirection could be accomplished on the client side using the error callback available when using jQuery for AJAX requests.  This would be nice, but in jQuery 1.5 and above a "statusCode" callback has been added that is even nicer.  You can use the callback like this:</p>

<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb7851'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb7851','cb3690'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb7851" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$.ajax({</li><li style="" class="li2">&nbsp; statusCode: {</li><li style="" class="li1">&nbsp; &nbsp; 404: function() {</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; alert('page not found');</li><li style="" class="li1">&nbsp; &nbsp; }</li><li style="" class="li2">&nbsp; }</li><li style="" class="li1">});</li></ol></div><div id="cb3690" style="display: none; color: red;"></div></div></div>

<p>The ease with which this allows you to define behavior for different status codes is fantastic.  As I set off down this path, the most obvious choice seemed to be adding a 401 (unauthorized) status code to the login page, but this got us into a weird redirect loop because forms authentication redirects all 401's to the login page - causing you lose the return URL, and redirect users back to the login page once they are authenticated.  Not exactly a paragon of usability.</p>

<p>Having found this out the hard way, I decided a custom status code might be better.  It's easy enough to add the custom status code to the login page with a single line of C#:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb18653'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb18653','cb21588'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb18653" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Response.<span style="color: #0000FF;">StatusCode</span> = <span style="color: #FF0000;">999</span>;</li></ol></div><div id="cb21588" style="display: none; color: red;"></div></div></div>

<p>We can then make our requests using something along these lines:</p>

<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb77644'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb77644','cb808'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb77644" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$.ajax({</li><li style="" class="li2">&nbsp; &nbsp; type: 'POST',</li><li style="" class="li1">&nbsp; &nbsp; url: '/Task/SignUp',</li><li style="" class="li2">&nbsp; &nbsp; data: 'projectId=' + pid + '&amp;storyId=' + sid + '&amp;id=' + id + '&amp;initials=' + initials,</li><li style="" class="li1">&nbsp; &nbsp; success: function (html) {</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; $('#' + id).replaceWith(html);</li><li style="" class="li1">&nbsp; &nbsp; },</li><li style="" class="li2">&nbsp; &nbsp; statusCode: {</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; 999: function() {</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; location.href = '/Account/LogOn?returnUrl=' + location.href;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; }</li><li style="" class="li2">&nbsp; &nbsp; }</li><li style="" class="li1">});</li></ol></div><div id="cb808" style="display: none; color: red;"></div></div></div>

<p>This works well enough, at least from cassini.  When deployed to an IIS server however, we noticed that the 999 status code was getting picked up by the error handling modules, and of course we did not have an error page set up for that code.  To get around that we had to add the following to the code to display our login view:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb82932'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb82932','cb31219'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb82932" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Response.<span style="color: #0000FF;">TrySkipIisCustomErrors</span> = <span style="color: #0600FF;">true</span>;</li></ol></div><div id="cb31219" style="display: none; color: red;"></div></div></div>

<p>That's kind of nasty, but it seems to allow us to accomplish our goal.  I think I can stomach it on this one page in the name of improving the user's experience on the site.  </p>

<p>There's one more thing we can do to make our lives easier.  There is no special behavior in our statusCode handler, so it would be nice define it only once.  Luckily, the folks at jQuery are a step ahead of us.  We can define our statusCode handler using the ajaxSetup method in our master page:</p>

<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb58081'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb58081','cb14126'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb58081" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$.ajaxSetup({</li><li style="" class="li2">&nbsp; &nbsp; statusCode: {</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; 999: function () {</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; location.href = '/Account/LogOn?returnUrl=' + location.href;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; }</li><li style="" class="li2">&nbsp; &nbsp; }</li><li style="" class="li1">});</li></ol></div><div id="cb14126" style="display: none; color: red;"></div></div></div>

<p>Now that everything is set up, we are properly redirected to the login page:</p>

<div class="image_block"><a href="http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/good-logon.PNG?mtime=1328987447"><img alt="" src="http://blogs.lessthandot.com/media/blogs/WebDev/handling-unauthenticated-ajax-requests/good-logon.PNG?mtime=1328987447" width="557" height="435" /></a></div>

<p>I'm not sure this is the best solution to our problem, but it is certainly a solution.  It allows us to keep leveraging ASP.net's built in error handling and authentication (I know they aren't perfect, but they are good enough for us in this scenario) while making the user's life a bit easier in the event something goes wrong.  In this case, my team is the primary user of the application so it makes our lives easier <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p>

<p>Complete source code for the application in question is available on <a href="https://github.com/jawsthegame/PivotalExtension">github</a>.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/handling-unauthenticated-ajax-requests">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/handling-unauthenticated-ajax-requests#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1624</wfw:commentRss>
		</item>
				<item>
			<title>Inverse Mapped Collections and NHibernate's Second-Level Cache</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/inverse-mapped-collections-and-nhibernate-s-second-level-cache</link>
			<pubDate>Mon, 14 Feb 2011 13:42:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>			<guid isPermaLink="false">1113@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;In order to simplify saving some entities, I recently changed some mappings in my application to use the &lt;strong&gt;inverse&lt;/strong&gt; attribute.  In less cryptic terms, this means that the collection item is responsible for maintaining the relationship to the collection owner.  In contrast, when using a standard relationship the parent entity is responsible for managing the collection items.  The reason for this is that it lets you save the collection items without going to the database (or second level cache) to get the collection&#039;s owner.&lt;/p&gt;

&lt;p&gt;This can make things a little weird - the primary difference is that both sides of the relationship now need to be aware that they are participants.  Say you have the following two classes, and the corresponding mappings (sorry, I used Fluent NHibernate):&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb2420&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; Recipe &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; Name &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; IList&amp;lt;IngredientUse&amp;gt; IngredientsUsed &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; IngredientUse &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;double&lt;/span&gt; Quantity &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; Ingredient Ingredient &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; RecipeMap : ClassMap&amp;lt;Recipe&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;r =&amp;gt; r.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Map &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;r =&amp;gt; r.&lt;span style=&quot;color: #0000FF;&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; HasMany &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;i =&amp;gt; i.&lt;span style=&quot;color: #0000FF;&quot;&gt;IngredientsUsed&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; IngredientUseMap : ClassMap&amp;lt;IngredientUse&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Map &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Quantity&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; References &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Ingredient&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb77312&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is pretty straightforward, but it does do a little bit of magic behind the scenes.  For example, even though the IngredientUse entity is not referencing the Recipe, NHibernate will expect a column called &quot;Recipe_id&quot; on the IngredientUse table in the database that it can use to maintain the relationship between the two entities.  &lt;/p&gt;

&lt;p&gt;When using an inverse relationship, a few changes are needed.  First, you map the collection (from the Recipe&#039;s perspective) using the inverse attribute.  Also, you need to reference the Recipe from the Ingredient (and the associated mapping).  So the inverse mappings look like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb33088&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; Recipe &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; Name &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; IList&amp;lt;IngredientUse&amp;gt; IngredientsUsed &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; IngredientUse &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;double&lt;/span&gt; Quantity &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; Ingredient Ingredient &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; Recipe Recipe &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; RecipeMap : ClassMap&amp;lt;Recipe&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;r =&amp;gt; r.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Map &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;r =&amp;gt; r.&lt;span style=&quot;color: #0000FF;&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; HasMany &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;i =&amp;gt; i.&lt;span style=&quot;color: #0000FF;&quot;&gt;IngredientsUsed&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Inverse&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; RecipeMap : ClassMap&amp;lt;IngredientUse&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Id &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Map &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Quantity&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; References &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Ingredient&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; References &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;iu =&amp;gt; iu.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb31329&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It&#039;s important to note that the updated mapping expects the exact same underlying DB structure.  So the model is starting to look a bit more like the database.  What this gets you is the ability to save an IngredientUse without first needing to retrieve the recipe you want to add it to.  So instead of saving it like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb10169&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Save &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IngredientUse ingredientUse, &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; recipeId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var recipe = session.&lt;span style=&quot;color: #0000FF;&quot;&gt;Get&lt;/span&gt;&amp;lt;Recipe&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;recipeId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; recipe.&lt;span style=&quot;color: #0000FF;&quot;&gt;AddIngredientUse&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ingredientUse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; session.&lt;span style=&quot;color: #0000FF;&quot;&gt;SaveOrUpdate&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;recipe&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb15582&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You now save it like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb22081&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Save &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IngredientUse ingredientUse, &lt;span style=&quot;color: #FF0000;&quot;&gt;long&lt;/span&gt; recipeId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; ingredientUse.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt; = session.&lt;span style=&quot;color: #0000FF;&quot;&gt;Load&lt;/span&gt;&amp;lt;Recipe&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;recipeId&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; session.&lt;span style=&quot;color: #0000FF;&quot;&gt;SaveOrUpdate&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ingredientUse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb34041&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The difference between Load and Get in this case is worth noting.  Load will NEVER return null, it will get your entity or throw an exception.  Get on the other hand can return null.  So when you use Load, you are essentially telling NHibernate &quot;this entity IS in the database, trust me&quot;.  That enables NHibernate to use a proxy instead of going out to make sure your entity actually exists.  And since we only need the Id, that proxy will never need to go out and load any of the actual object data.  I found this especially useful when saving an IngredientUse at the end of an HTTP request.&lt;/p&gt;

&lt;p&gt;A perfectly logical side effect (that I didn&#039;t consider) to this change is the presence of stale results in NHibernate&#039;s second level cache.  Because the Recipe&#039;s associated collections are cached in relation to the Recipe, and the Recipe is never saved, the collection does not get updated in the cache.  I thought I was stuck for a bit, until I found the Session Factory&#039;s &lt;a href=&quot;http://ajava.org/online/hibernate3api/org/hibernate/SessionFactory.html#evictCollection%28java.lang.String,%20java.io.Serializable%29&quot;&gt;&quot;EvictCollection&quot;&lt;/a&gt; method.  This is one of those &quot;use at your own risk&quot; methods, so consider yourself warned.  If abused, this could get pretty ugly (leading to a situation where you&#039;d be better off not caching your collections at all).  However, I&#039;ve got an app where I expect far more reads than writes, and I don&#039;t think I&#039;ll be using it all over the place, so I think this method can be leveraged to solve my problem.&lt;/p&gt;

&lt;p&gt;The EvictCollection method&#039;s overload that we are interested in takes a string representing the fully qualified collection name, and an object representing the Id of the &lt;strong&gt;parent object&lt;/strong&gt; (in this case the Recipe).  So the call we need to make looks something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb29678&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;sessionFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;EvictCollection&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;MyApp.Domain.Recipe.IngredientsUsed&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #FF0000;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb93940&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Easy enough, but the approach leaves a bit to be desired.  Because of the magical string, this approach will not be refactoring-friendly, and prone to runtime errors.  By using a few LINQ expressions we can do a bit better though.  We&#039;ll create a class called CollectionEvictor to take care of this stuff for us.  But first we&#039;ll write a test.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb17012&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Evict &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var mockery = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; MockRepository &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var sessionFactory = mockery.&lt;span style=&quot;color: #0000FF;&quot;&gt;StrictMock&lt;/span&gt;&amp;lt;ISessionFactory&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var ingredientUse = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; IngredientUse &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Recipe = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Recipe &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; Id = &lt;span style=&quot;color: #FF0000;&quot;&gt;1&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;mockery.&lt;span style=&quot;color: #0000FF;&quot;&gt;Record&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sessionFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;EvictCollection&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;MyApp.Domain.Recipe.IngredientsUsed&amp;quot;&lt;/span&gt;, additionUse.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;mockery.&lt;span style=&quot;color: #0000FF;&quot;&gt;Playback&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var evictor = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CollectionEvictor&amp;lt;IngredientUse&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&amp;gt; x.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IngredientsUsed&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; x =&amp;gt; x.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sessionFactory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; evictor.&lt;span style=&quot;color: #0000FF;&quot;&gt;Evict&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;additionUse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb15754&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the class is pretty straight forward as well:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb73871&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; CollectionEvictor&amp;lt;T&amp;gt; where T : &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; MemberExpression collectionProperty;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; Func&amp;lt;T, object&amp;gt; idFunction;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; ISessionFactory sessionFactory;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; CollectionEvictor &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt; collectionProperty, Func&amp;lt;T, object&amp;gt; idFunction&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; : &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;collectionProperty, idFunction, ObjectFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;ISessionFactory&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; CollectionEvictor &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt; collectionProperty, Func&amp;lt;T, object&amp;gt; idFunction, ISessionFactory sessionFactory&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;collectionProperty&lt;/span&gt; = collectionProperty.&lt;span style=&quot;color: #0000FF;&quot;&gt;Body&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;as&lt;/span&gt; MemberExpression;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;idFunction&lt;/span&gt; = idFunction;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;sessionFactory&lt;/span&gt; = sessionFactory;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Evict &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;T updated&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var key = collectionProperty.&lt;span style=&quot;color: #0000FF;&quot;&gt;Expression&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Type&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;FullName&lt;/span&gt; + &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt; + collectionProperty.&lt;span style=&quot;color: #0000FF;&quot;&gt;Member&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Name&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sessionFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;EvictCollection&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;key, idFunction.&lt;span style=&quot;color: #0000FF;&quot;&gt;Invoke&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;updated&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb98025&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now we&#039;ve got something that&#039;s a bit awkward, but at least checked at compile time.  It seems strange that we need to go through the IngredientUse&#039;s link back to the recipe to specify the collection, but that is one of the things that allows us to do it in a generic fashion.  The key to making this happen is the LINQ expression type, that allows us to store the lambda expression as &lt;strong&gt;data&lt;/strong&gt; instead of as executable code, allowing us to get much more information about it&#039;s signature.  By taking its body and casting it to a MemberExpression (thanks &lt;a href=&quot;http://blogs.lessthandot.com/index.php/All/?disp=authdir&amp;amp;author=225&quot;&gt;Matt!&lt;/a&gt;) it becomes easy to get the full name of the Recipe type, and the name of the property referenced in the function.  We can then put these together and get our magical string.  Because we don&#039;t need to execute anything but the call to get the attached Recipe&#039;s Id, we don&#039;t need to load anything from the database and can use the information our proxy already has.&lt;/p&gt;

&lt;p&gt;Assuming there&#039;s already a base repository defined, it can be extended like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb64208&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; abstract &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; CollectionEvictingRepository&amp;lt;T&amp;gt; : BaseRepository&amp;lt;T&amp;gt; where T : &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt;, &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;readonly&lt;/span&gt; IList&amp;lt;CollectionEvictor&amp;lt;T&amp;gt;&amp;gt; evictors = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; List&amp;lt;CollectionEvictor&amp;lt;T&amp;gt;&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Save &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;T toSave&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;base&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Save&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toSave&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; EvictCollections &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toSave&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Remove &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;T toRemove&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;base&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Remove&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toRemove&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; EvictCollections &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toRemove&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; EvictCollections &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;T toSave&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;foreach&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;var evictor &lt;span style=&quot;color: #0600FF;&quot;&gt;in&lt;/span&gt; evictors&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; evictor.&lt;span style=&quot;color: #0000FF;&quot;&gt;Evict&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;toSave&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb44490&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, this calls the standard Save/Delete behavior, then evicts any collections that have been invalidated as a result of the operation.  Implementations can then look like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb11294&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;sealed&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; AdditionUseRepository : CollectionEvictingRepository&amp;lt;AdditionUse&amp;gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; AdditionUseRepository&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; evictors.&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; CollectionEvictor&amp;lt;IngredientUse&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; instance =&amp;gt; instance.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IngredientsUsed&lt;/span&gt;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; instance =&amp;gt; instance.&lt;span style=&quot;color: #0000FF;&quot;&gt;Recipe&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb74711&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All told this is a pretty naive implementation (You&#039;ll need to check for null after casting to MemberExpression, etc...) but I think it shows the idea pretty well.  You may not ever need to use an Inverse relationship, but if you do, and need to pair it with a second level cache, something like this might come in handy.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/inverse-mapped-collections-and-nhibernate-s-second-level-cache&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>In order to simplify saving some entities, I recently changed some mappings in my application to use the <strong>inverse</strong> attribute.  In less cryptic terms, this means that the collection item is responsible for maintaining the relationship to the collection owner.  In contrast, when using a standard relationship the parent entity is responsible for managing the collection items.  The reason for this is that it lets you save the collection items without going to the database (or second level cache) to get the collection's owner.</p>

<p>This can make things a little weird - the primary difference is that both sides of the relationship now need to be aware that they are participants.  Say you have the following two classes, and the corresponding mappings (sorry, I used Fluent NHibernate):</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb46210'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb46210','cb85901'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb46210" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #FF0000;">class</span> Recipe <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">long</span> Id <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">string</span> Name <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; IList&lt;IngredientUse&gt; IngredientsUsed <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #FF0000;">class</span> IngredientUse <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">long</span> Id <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">double</span> Quantity <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> Ingredient Ingredient <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #FF0000;">class</span> RecipeMap : ClassMap&lt;Recipe&gt; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; Id <span style="color: #000000;">&#40;</span>r =&gt; r.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; Map <span style="color: #000000;">&#40;</span>r =&gt; r.<span style="color: #0000FF;">Name</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; HasMany <span style="color: #000000;">&#40;</span>i =&gt; i.<span style="color: #0000FF;">IngredientsUsed</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #FF0000;">class</span> IngredientUseMap : ClassMap&lt;IngredientUse&gt; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; Id <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; Map <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Quantity</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; References <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Ingredient</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb85901" style="display: none; color: red;"></div></div></div>

<p>This is pretty straightforward, but it does do a little bit of magic behind the scenes.  For example, even though the IngredientUse entity is not referencing the Recipe, NHibernate will expect a column called "Recipe_id" on the IngredientUse table in the database that it can use to maintain the relationship between the two entities.  </p>

<p>When using an inverse relationship, a few changes are needed.  First, you map the collection (from the Recipe's perspective) using the inverse attribute.  Also, you need to reference the Recipe from the Ingredient (and the associated mapping).  So the inverse mappings look like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb28288'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb28288','cb9127'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb28288" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #FF0000;">class</span> Recipe <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">long</span> Id <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">string</span> Name <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; IList&lt;IngredientUse&gt; IngredientsUsed <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #FF0000;">class</span> IngredientUse <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">long</span> Id <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">double</span> Quantity <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> Ingredient Ingredient <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> Recipe Recipe <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #FF0000;">class</span> RecipeMap : ClassMap&lt;Recipe&gt; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; Id <span style="color: #000000;">&#40;</span>r =&gt; r.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; Map <span style="color: #000000;">&#40;</span>r =&gt; r.<span style="color: #0000FF;">Name</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; HasMany <span style="color: #000000;">&#40;</span>i =&gt; i.<span style="color: #0000FF;">IngredientsUsed</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Inverse</span> <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #FF0000;">class</span> RecipeMap : ClassMap&lt;IngredientUse&gt; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; Id <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; Map <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Quantity</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; References <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Ingredient</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; References <span style="color: #000000;">&#40;</span>iu =&gt; iu.<span style="color: #0000FF;">Recipe</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb9127" style="display: none; color: red;"></div></div></div>

<p>It's important to note that the updated mapping expects the exact same underlying DB structure.  So the model is starting to look a bit more like the database.  What this gets you is the ability to save an IngredientUse without first needing to retrieve the recipe you want to add it to.  So instead of saving it like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb85209'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb85209','cb90346'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb85209" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">void</span> Save <span style="color: #000000;">&#40;</span>IngredientUse ingredientUse, <span style="color: #FF0000;">long</span> recipeId<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var recipe = session.<span style="color: #0000FF;">Get</span>&lt;Recipe&gt; <span style="color: #000000;">&#40;</span>recipeId<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; recipe.<span style="color: #0000FF;">AddIngredientUse</span> <span style="color: #000000;">&#40;</span>ingredientUse<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; session.<span style="color: #0000FF;">SaveOrUpdate</span> <span style="color: #000000;">&#40;</span>recipe<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb90346" style="display: none; color: red;"></div></div></div>

<p>You now save it like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb91168'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb91168','cb32969'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb91168" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">void</span> Save <span style="color: #000000;">&#40;</span>IngredientUse ingredientUse, <span style="color: #FF0000;">long</span> recipeId<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; ingredientUse.<span style="color: #0000FF;">Recipe</span> = session.<span style="color: #0000FF;">Load</span>&lt;Recipe&gt; <span style="color: #000000;">&#40;</span>recipeId<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; session.<span style="color: #0000FF;">SaveOrUpdate</span> <span style="color: #000000;">&#40;</span>ingredientUse<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb32969" style="display: none; color: red;"></div></div></div>

<p>The difference between Load and Get in this case is worth noting.  Load will NEVER return null, it will get your entity or throw an exception.  Get on the other hand can return null.  So when you use Load, you are essentially telling NHibernate "this entity IS in the database, trust me".  That enables NHibernate to use a proxy instead of going out to make sure your entity actually exists.  And since we only need the Id, that proxy will never need to go out and load any of the actual object data.  I found this especially useful when saving an IngredientUse at the end of an HTTP request.</p>

<p>A perfectly logical side effect (that I didn't consider) to this change is the presence of stale results in NHibernate's second level cache.  Because the Recipe's associated collections are cached in relation to the Recipe, and the Recipe is never saved, the collection does not get updated in the cache.  I thought I was stuck for a bit, until I found the Session Factory's <a href="http://ajava.org/online/hibernate3api/org/hibernate/SessionFactory.html#evictCollection%28java.lang.String,%20java.io.Serializable%29">"EvictCollection"</a> method.  This is one of those "use at your own risk" methods, so consider yourself warned.  If abused, this could get pretty ugly (leading to a situation where you'd be better off not caching your collections at all).  However, I've got an app where I expect far more reads than writes, and I don't think I'll be using it all over the place, so I think this method can be leveraged to solve my problem.</p>

<p>The EvictCollection method's overload that we are interested in takes a string representing the fully qualified collection name, and an object representing the Id of the <strong>parent object</strong> (in this case the Recipe).  So the call we need to make looks something like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb47775'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb47775','cb94840'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb47775" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">sessionFactory.<span style="color: #0000FF;">EvictCollection</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;MyApp.Domain.Recipe.IngredientsUsed&quot;</span>, <span style="color: #FF0000;">1</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb94840" style="display: none; color: red;"></div></div></div>

<p>Easy enough, but the approach leaves a bit to be desired.  Because of the magical string, this approach will not be refactoring-friendly, and prone to runtime errors.  By using a few LINQ expressions we can do a bit better though.  We'll create a class called CollectionEvictor to take care of this stuff for us.  But first we'll write a test.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb15633'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb15633','cb83297'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb15633" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Evict <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; var mockery = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> MockRepository <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; var sessionFactory = mockery.<span style="color: #0000FF;">StrictMock</span>&lt;ISessionFactory&gt; <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; var ingredientUse = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> IngredientUse <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Recipe = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Recipe <span style="color: #000000;">&#123;</span> Id = <span style="color: #FF0000;">1</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>mockery.<span style="color: #0000FF;">Record</span> <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; sessionFactory.<span style="color: #0000FF;">EvictCollection</span> <span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;MyApp.Domain.Recipe.IngredientsUsed&quot;</span>, additionUse.<span style="color: #0000FF;">Recipe</span>.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>mockery.<span style="color: #0000FF;">Playback</span> <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var evictor = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CollectionEvictor&lt;IngredientUse&gt; <span style="color: #000000;">&#40;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x =&gt; x.<span style="color: #0000FF;">Recipe</span>.<span style="color: #0000FF;">IngredientsUsed</span>,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x =&gt; x.<span style="color: #0000FF;">Recipe</span>.<span style="color: #0000FF;">Id</span>,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sessionFactory<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; evictor.<span style="color: #0000FF;">Evict</span><span style="color: #000000;">&#40;</span>additionUse<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb83297" style="display: none; color: red;"></div></div></div>

<p>And the class is pretty straight forward as well:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb73209'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb73209','cb18053'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb73209" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> CollectionEvictor&lt;T&gt; where T : <span style="color: #FF0000;">class</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> MemberExpression collectionProperty;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> Func&lt;T, object&gt; idFunction;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">readonly</span> ISessionFactory sessionFactory;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> CollectionEvictor <span style="color: #000000;">&#40;</span>Expression&lt;Func&lt;T, object&gt;&gt; collectionProperty, Func&lt;T, object&gt; idFunction<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; : <span style="color: #0600FF;">this</span> <span style="color: #000000;">&#40;</span>collectionProperty, idFunction, ObjectFactory.<span style="color: #0000FF;">GetInstance</span>&lt;ISessionFactory&gt; <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> CollectionEvictor <span style="color: #000000;">&#40;</span>Expression&lt;Func&lt;T, object&gt;&gt; collectionProperty, Func&lt;T, object&gt; idFunction, ISessionFactory sessionFactory<span style="color: #000000;">&#41;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">collectionProperty</span> = collectionProperty.<span style="color: #0000FF;">Body</span> <span style="color: #0600FF;">as</span> MemberExpression;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">idFunction</span> = idFunction;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">this</span>.<span style="color: #0000FF;">sessionFactory</span> = sessionFactory;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Evict <span style="color: #000000;">&#40;</span>T updated<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var key = collectionProperty.<span style="color: #0000FF;">Expression</span>.<span style="color: #0000FF;">Type</span>.<span style="color: #0000FF;">FullName</span> + <span style="color: #808080;">&quot;.&quot;</span> + collectionProperty.<span style="color: #0000FF;">Member</span>.<span style="color: #0000FF;">Name</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; sessionFactory.<span style="color: #0000FF;">EvictCollection</span> <span style="color: #000000;">&#40;</span>key, idFunction.<span style="color: #0000FF;">Invoke</span> <span style="color: #000000;">&#40;</span>updated<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb18053" style="display: none; color: red;"></div></div></div>

<p>And now we've got something that's a bit awkward, but at least checked at compile time.  It seems strange that we need to go through the IngredientUse's link back to the recipe to specify the collection, but that is one of the things that allows us to do it in a generic fashion.  The key to making this happen is the LINQ expression type, that allows us to store the lambda expression as <strong>data</strong> instead of as executable code, allowing us to get much more information about it's signature.  By taking its body and casting it to a MemberExpression (thanks <a href="http://blogs.lessthandot.com/index.php/All/?disp=authdir&amp;author=225">Matt!</a>) it becomes easy to get the full name of the Recipe type, and the name of the property referenced in the function.  We can then put these together and get our magical string.  Because we don't need to execute anything but the call to get the attached Recipe's Id, we don't need to load anything from the database and can use the information our proxy already has.</p>

<p>Assuming there's already a base repository defined, it can be extended like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb60609'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb60609','cb6297'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb60609" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> abstract <span style="color: #FF0000;">class</span> CollectionEvictingRepository&lt;T&gt; : BaseRepository&lt;T&gt; where T : <span style="color: #FF0000;">class</span>, <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">readonly</span> IList&lt;CollectionEvictor&lt;T&gt;&gt; evictors = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> List&lt;CollectionEvictor&lt;T&gt;&gt; <span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">override</span> <span style="color: #0600FF;">void</span> Save <span style="color: #000000;">&#40;</span>T toSave<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">base</span>.<span style="color: #0000FF;">Save</span> <span style="color: #000000;">&#40;</span>toSave<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; EvictCollections <span style="color: #000000;">&#40;</span>toSave<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">override</span> <span style="color: #0600FF;">void</span> Remove <span style="color: #000000;">&#40;</span>T toRemove<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">base</span>.<span style="color: #0000FF;">Remove</span> <span style="color: #000000;">&#40;</span>toRemove<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; EvictCollections <span style="color: #000000;">&#40;</span>toRemove<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">void</span> EvictCollections <span style="color: #000000;">&#40;</span>T toSave<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">foreach</span> <span style="color: #000000;">&#40;</span>var evictor <span style="color: #0600FF;">in</span> evictors<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evictor.<span style="color: #0000FF;">Evict</span> <span style="color: #000000;">&#40;</span>toSave<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb6297" style="display: none; color: red;"></div></div></div>

<p>As you can see, this calls the standard Save/Delete behavior, then evicts any collections that have been invalidated as a result of the operation.  Implementations can then look like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb49383'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb49383','cb70778'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb49383" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">sealed</span> <span style="color: #FF0000;">class</span> AdditionUseRepository : CollectionEvictingRepository&lt;AdditionUse&gt;</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> AdditionUseRepository<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> </li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; evictors.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> CollectionEvictor&lt;IngredientUse&gt;<span style="color: #000000;">&#40;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; instance =&gt; instance.<span style="color: #0000FF;">Recipe</span>.<span style="color: #0000FF;">IngredientsUsed</span>,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; instance =&gt; instance.<span style="color: #0000FF;">Recipe</span>.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb70778" style="display: none; color: red;"></div></div></div>

<p>All told this is a pretty naive implementation (You'll need to check for null after casting to MemberExpression, etc...) but I think it shows the idea pretty well.  You may not ever need to use an Inverse relationship, but if you do, and need to pair it with a second level cache, something like this might come in handy.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/inverse-mapped-collections-and-nhibernate-s-second-level-cache">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/inverse-mapped-collections-and-nhibernate-s-second-level-cache#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1113</wfw:commentRss>
		</item>
				<item>
			<title>Using Binding Redirects to Stay Out of DLL Hell</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/using-binding-redirects-to-stay-out-of-d</link>
			<pubDate>Mon, 25 Oct 2010 14:14:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>			<guid isPermaLink="false">992@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;We found ourselves in a tricky situation at work this week.  I&#039;m surprised it hadn&#039;t come up before, but I suppose our customers aren&#039;t always the type to move to a new technology quickly.  But we had a customer trying to install the back end of our application on a Windows 2008 server, and having trouble.  We use AzMan to get our application&#039;s permissions to work with windows authentication, and there was a new version included with server 2008.  Our application was compiled against the 2003 version, and it was having problems when installed in this environment.&lt;/p&gt;

&lt;p&gt;We first tried updating our references to use the 2008 version, and this seemed to work fine.  Until people tried to install the most recent builds on XP and 2003 servers the next day.  Needless to say, we wanted to avoid building different installers for different operating systems.  The application had been running fine simply using the AzMan version found in the server&#039;s GAC previously, so we decided to try and find a way to make it work that way.&lt;/p&gt;

&lt;p&gt;Eventually we came across the idea of a &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/eftw1fys.aspx&quot;&gt;Binding Redirect&lt;/a&gt; and thought this might be helpful.  It seems like the redirect only works when pointing to a newer version of a referenced DLL, so we first changed our references to use the 1.0 version of AzMan (from windows 2000) and kicked of a fresh build of the installer.  We set up our redirects like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb92757&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;?xml&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;version&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;encoding&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;configuration&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;configSections&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;&amp;lt;!-- snip --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/configSections&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;runtime&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;assemblyBinding&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;xmlns&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;urn:schemas-microsoft-com:asm.v1&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;dependentAssembly&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;assemblyIdentity&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Microsoft.Interop.Security.AzRoles&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000066;&quot;&gt;publicKeyToken&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;31bf3856ad364e35&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000066;&quot;&gt;culture&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;neutral&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;bindingRedirect&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;oldVersion&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.0.0.0&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #000066;&quot;&gt;newVersion&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;2.0.0.0&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;bindingRedirect&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;oldVersion&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.0.0.0&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #000066;&quot;&gt;newVersion&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.2.0.0&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/dependentAssembly&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/assemblyBinding&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/runtime&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;&amp;lt;!-- snip --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;configuration&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb89377&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At first it didn&#039;t work, but looking at the error we noticed that the app was looking for the 2.0 version of AzMan (on a 2003 box where we were expecting 1.2).  Wondering if the ordering of the redirects matters, we switched the two redirect lines and restarted IIS.  Lucky for us, it worked (on Windows 2008 R2, 2008 and 2003 sp2 boxes).  &lt;/p&gt;

&lt;p&gt;This isn&#039;t something I&#039;ve had to do a lot, and I&#039;m not sure it&#039;s something I&#039;d like to do too often (I may be overly obsessive about local references for a few reasons).  But for cases like this where you&#039;re working with a legacy component that is tightly bound to the OS AND maintains a pretty consistent interface across versions, it can certainly come in handy.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/using-binding-redirects-to-stay-out-of-d&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>We found ourselves in a tricky situation at work this week.  I'm surprised it hadn't come up before, but I suppose our customers aren't always the type to move to a new technology quickly.  But we had a customer trying to install the back end of our application on a Windows 2008 server, and having trouble.  We use AzMan to get our application's permissions to work with windows authentication, and there was a new version included with server 2008.  Our application was compiled against the 2003 version, and it was having problems when installed in this environment.</p>

<p>We first tried updating our references to use the 2008 version, and this seemed to work fine.  Until people tried to install the most recent builds on XP and 2003 servers the next day.  Needless to say, we wanted to avoid building different installers for different operating systems.  The application had been running fine simply using the AzMan version found in the server's GAC previously, so we decided to try and find a way to make it work that way.</p>

<p>Eventually we came across the idea of a <a href="http://msdn.microsoft.com/en-us/library/eftw1fys.aspx">Binding Redirect</a> and thought this might be helpful.  It seems like the redirect only works when pointing to a newer version of a referenced DLL, so we first changed our references to use the 1.0 version of AzMan (from windows 2000) and kicked of a fresh build of the installer.  We set up our redirects like so:</p>

<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb20064'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb20064','cb78434'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb20064" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;utf-8&quot;</span><span style="font-weight: bold; color: black;">?&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;configuration<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;configSections<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #808080; font-style: italic;">&lt;!-- snip --&gt;</span></span></li><li style="" class="li1">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/configSections<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;runtime<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;assemblyBinding</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">&quot;urn:schemas-microsoft-com:asm.v1&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;dependentAssembly<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;assemblyIdentity</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;Microsoft.Interop.Security.AzRoles&quot;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066;">publicKeyToken</span>=<span style="color: #ff0000;">&quot;31bf3856ad364e35&quot;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066;">culture</span>=<span style="color: #ff0000;">&quot;neutral&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;bindingRedirect</span> <span style="color: #000066;">oldVersion</span>=<span style="color: #ff0000;">&quot;1.0.0.0&quot;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #000066;">newVersion</span>=<span style="color: #ff0000;">&quot;2.0.0.0&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;bindingRedirect</span> <span style="color: #000066;">oldVersion</span>=<span style="color: #ff0000;">&quot;1.0.0.0&quot;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #000066;">newVersion</span>=<span style="color: #ff0000;">&quot;1.2.0.0&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/dependentAssembly<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/assemblyBinding<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/runtime<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #808080; font-style: italic;">&lt;!-- snip --&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;configuration<span style="font-weight: bold; color: black;">&gt;</span></span></span></li></ol></div><div id="cb78434" style="display: none; color: red;"></div></div></div>

<p>At first it didn't work, but looking at the error we noticed that the app was looking for the 2.0 version of AzMan (on a 2003 box where we were expecting 1.2).  Wondering if the ordering of the redirects matters, we switched the two redirect lines and restarted IIS.  Lucky for us, it worked (on Windows 2008 R2, 2008 and 2003 sp2 boxes).  </p>

<p>This isn't something I've had to do a lot, and I'm not sure it's something I'd like to do too often (I may be overly obsessive about local references for a few reasons).  But for cases like this where you're working with a legacy component that is tightly bound to the OS AND maintains a pretty consistent interface across versions, it can certainly come in handy.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/using-binding-redirects-to-stay-out-of-d">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/using-binding-redirects-to-stay-out-of-d#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=992</wfw:commentRss>
		</item>
				<item>
			<title>Some Thoughts on Session Management</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/some-thoughts-on-session-management</link>
			<pubDate>Mon, 16 Aug 2010 21:31:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">ASP.NET</category>			<guid isPermaLink="false">930@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;The last time I did a post on NHibernate (or any post for that matter - I guess I&#039;ve been a bit busy) &lt;a href=&quot;http://blogs.lessthandot.com/index.php/DesktopDev/MSTech/two-years-with-nhibernate-lessons-learne#c3958&quot;&gt;Ben&lt;/a&gt; asked a question about what I ended up using for session management in the application I&#039;ve been working on.  I guess I could come out and answer it, but I&#039;d hardly get a new post out of that.  Instead I will break out the tried and true answer for all things IT, &quot;It depends&quot;.  Let&#039;s take a look at what it depends on.&lt;/p&gt;

&lt;h2&gt;What is the Session?&lt;/h2&gt;
&lt;p&gt;This is probably the best place to start.  If you&#039;re used to working with ADO.net, jdbc, or something similar, it&#039;s easy to think of the session as nothing more than a window into your database that allows you to execute queries, like an &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.data.idbconnection.aspx&quot;&gt;IDbConnection&lt;/a&gt;, but it does a lot more for you if you let it.  NHibernate sessions provide a great &lt;a href=&quot;http://martinfowler.com/eaaCatalog/unitOfWork.html&quot;&gt;Unit of Work&lt;/a&gt; container, allowing you a great deal of control over how things get written to your database.  Unless you are using a stateless session (a session configured not to hold anything in memory) the session will also use NHibernate&#039;s first level cache to store objects that are in use, helping you to avoid excessive trips to the database.&lt;/p&gt;

&lt;h2&gt;What is your Unit of Work?&lt;/h2&gt;
&lt;p&gt;This is the $64,000 question.  Once you know what your unit of work is, session management will more or less solve itself.  As you can imagine, this is highly dependent on the nature of the application you&#039;re working on.  In most cases the unit of work is synonymous with a business transaction.  It represents the minimum amount of work that you want to commit to your underlying storage mechanism.  This is an all or nothing proposition - if your unit of work requires you to write four objects to a data store, and only three succeed, then the three that succeeded will be cancelled, or rolled back.  &lt;/p&gt;

&lt;p&gt;Keeping this in mind, a desktop application using an embedded database for persistence may be able to get by with a single session for the life of the application, as long as transactions are properly committed and the session is flushed when the app shuts down.  This gives you the benefit of storing a LOT of data in the first level cache, which can be very beneficial when you&#039;re positive no one else will be modifying it (though, with an in memory database you may not even need the first level cache &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif&quot; title=&quot;;)&quot; alt=&quot;;)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt; ).  &lt;/p&gt;

&lt;p&gt;At the other end of the spectrum you could find SOA type applications where the unit of work is performed across several round trips between client and server.  Using this strategy, subsequent requests to the server need to be able to find their way back to the same session that their work was started with, otherwise they won&#039;t have the data they need.  In most cases I&#039;d imagine this requires a more complete unit of work implementation that can be persisted across requests and use the NHibernate session under the hood.  &lt;/p&gt;

&lt;h2&gt;What about Me?&lt;/h2&gt;
&lt;p&gt;Like most people, I find myself somewhere in the middle of this spectrum.  My application is web-based, and the primary unit of work can be completed in a single request, so I ended up using a session per request strategy.  I found that &lt;a href=&quot;http://structuremap.github.com/structuremap/index.html&quot;&gt;StructureMap&lt;/a&gt; makes managing this a lot easier.  I need to first register a Session Factory (as a singleton) with StructureMap.  I then tell StructureMap how to retrieve an ISession from this factory (using the ConstructedBy method), and also ensure that it caches sessions &lt;em&gt;per HTTP context&lt;/em&gt;.  This is all registered from the application layer, but consumed farther down in the persistence layer.  Finally, in Application_EndRequest I call a function to clean up anything that StructureMap has cached by HTTP context.  &lt;/p&gt;

&lt;p&gt;I mentioned that this was the &quot;primary&quot; unit of work, there is one area that this does not cover, and that is authentication.  For a while I used built in providers (for SQL Server, then the MySQL provider in MySQL.Data), but when I moved databases again (to postgres) I decided it was time to make a change.  Now I&#039;m using a &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.web.security.membershipprovider.aspx&quot;&gt;MembershipProvider&lt;/a&gt; based on NHibernate, to ensure that it is as easy to move my authentication mechanism across database platforms as it is to move the domain logic.  To me, using the session per request strategy didn&#039;t really make a lot of sense here because the provider isn&#039;t really tied to the HTTP context, but to the application itself.  In addition, all data used by the authentication process can ONLY be changed through the membership provider.  So, for authentication I keep a single session open for the entire life of the application.  This would present some interesting challenges if I were to need to scale across multiple servers, but the code changes to the provider will not be too difficult.  In the meantime, I see some pretty good performance benefits from using the single session, as the provider rarely needs to hit the database, or even the second level cache (memcached).&lt;/p&gt;

&lt;p&gt;In summary, I find it difficult to offer advice about session management without knowing more about the application.  Others have already explained the underlying concepts far better than I could ever hope to, so in a way writing this post feels like a complete waste.  But I like writing about my thought process when it comes to matters like these, so I did enjoy writing it.  And I think there is some value in sharing this thought process with others, so hopefully at least a few people get something out of reading it.  Especially Ben, if he&#039;s still reading.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/some-thoughts-on-session-management&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>The last time I did a post on NHibernate (or any post for that matter - I guess I've been a bit busy) <a href="http://blogs.lessthandot.com/index.php/DesktopDev/MSTech/two-years-with-nhibernate-lessons-learne#c3958">Ben</a> asked a question about what I ended up using for session management in the application I've been working on.  I guess I could come out and answer it, but I'd hardly get a new post out of that.  Instead I will break out the tried and true answer for all things IT, "It depends".  Let's take a look at what it depends on.</p>

<h2>What is the Session?</h2>
<p>This is probably the best place to start.  If you're used to working with ADO.net, jdbc, or something similar, it's easy to think of the session as nothing more than a window into your database that allows you to execute queries, like an <a href="http://msdn.microsoft.com/en-us/library/system.data.idbconnection.aspx">IDbConnection</a>, but it does a lot more for you if you let it.  NHibernate sessions provide a great <a href="http://martinfowler.com/eaaCatalog/unitOfWork.html">Unit of Work</a> container, allowing you a great deal of control over how things get written to your database.  Unless you are using a stateless session (a session configured not to hold anything in memory) the session will also use NHibernate's first level cache to store objects that are in use, helping you to avoid excessive trips to the database.</p>

<h2>What is your Unit of Work?</h2>
<p>This is the $64,000 question.  Once you know what your unit of work is, session management will more or less solve itself.  As you can imagine, this is highly dependent on the nature of the application you're working on.  In most cases the unit of work is synonymous with a business transaction.  It represents the minimum amount of work that you want to commit to your underlying storage mechanism.  This is an all or nothing proposition - if your unit of work requires you to write four objects to a data store, and only three succeed, then the three that succeeded will be cancelled, or rolled back.  </p>

<p>Keeping this in mind, a desktop application using an embedded database for persistence may be able to get by with a single session for the life of the application, as long as transactions are properly committed and the session is flushed when the app shuts down.  This gives you the benefit of storing a LOT of data in the first level cache, which can be very beneficial when you're positive no one else will be modifying it (though, with an in memory database you may not even need the first level cache <img src="http://blogs.lessthandot.com/rsc/smilies/icon_wink.gif" title=";)" alt=";)" class="middle" width="15" height="15" /> ).  </p>

<p>At the other end of the spectrum you could find SOA type applications where the unit of work is performed across several round trips between client and server.  Using this strategy, subsequent requests to the server need to be able to find their way back to the same session that their work was started with, otherwise they won't have the data they need.  In most cases I'd imagine this requires a more complete unit of work implementation that can be persisted across requests and use the NHibernate session under the hood.  </p>

<h2>What about Me?</h2>
<p>Like most people, I find myself somewhere in the middle of this spectrum.  My application is web-based, and the primary unit of work can be completed in a single request, so I ended up using a session per request strategy.  I found that <a href="http://structuremap.github.com/structuremap/index.html">StructureMap</a> makes managing this a lot easier.  I need to first register a Session Factory (as a singleton) with StructureMap.  I then tell StructureMap how to retrieve an ISession from this factory (using the ConstructedBy method), and also ensure that it caches sessions <em>per HTTP context</em>.  This is all registered from the application layer, but consumed farther down in the persistence layer.  Finally, in Application_EndRequest I call a function to clean up anything that StructureMap has cached by HTTP context.  </p>

<p>I mentioned that this was the "primary" unit of work, there is one area that this does not cover, and that is authentication.  For a while I used built in providers (for SQL Server, then the MySQL provider in MySQL.Data), but when I moved databases again (to postgres) I decided it was time to make a change.  Now I'm using a <a href="http://msdn.microsoft.com/en-us/library/system.web.security.membershipprovider.aspx">MembershipProvider</a> based on NHibernate, to ensure that it is as easy to move my authentication mechanism across database platforms as it is to move the domain logic.  To me, using the session per request strategy didn't really make a lot of sense here because the provider isn't really tied to the HTTP context, but to the application itself.  In addition, all data used by the authentication process can ONLY be changed through the membership provider.  So, for authentication I keep a single session open for the entire life of the application.  This would present some interesting challenges if I were to need to scale across multiple servers, but the code changes to the provider will not be too difficult.  In the meantime, I see some pretty good performance benefits from using the single session, as the provider rarely needs to hit the database, or even the second level cache (memcached).</p>

<p>In summary, I find it difficult to offer advice about session management without knowing more about the application.  Others have already explained the underlying concepts far better than I could ever hope to, so in a way writing this post feels like a complete waste.  But I like writing about my thought process when it comes to matters like these, so I did enjoy writing it.  And I think there is some value in sharing this thought process with others, so hopefully at least a few people get something out of reading it.  Especially Ben, if he's still reading.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/some-thoughts-on-session-management">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/some-thoughts-on-session-management#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=930</wfw:commentRss>
		</item>
				<item>
			<title>Using Multiple jQuery UI Sliders on a Single Page</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/using-multiple-jquery-ui-sliders-on-a-si</link>
			<pubDate>Thu, 10 Jun 2010 14:00:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Web Design, Graphics &amp; Styling</category>
<category domain="alt">Javascript</category>			<guid isPermaLink="false">870@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;I had a bit of frustration lately when dealing with jQuery UI&#039;s slider widget, more specifically when trying to deal with many on the same page.  I wanted to follow the same pattern shown in the example for &lt;a href=&quot;http://jqueryui.com/demos/slider/#steps&quot;&gt;snap to increments&lt;/a&gt; where I have a div for the slider, and the value is actually set to a text input for easy form submission.  The individual elements look something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;html&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;html&quot; id=&quot;cb73889&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/div.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;div&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;class&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;SliderControl&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/label.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;label&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;for&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;clarity&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Clarity:&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/input.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;input&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;class&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;SliderText&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;readonly&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;readonly&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;clarity&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;clarity&amp;quot;&lt;/span&gt;/&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/div.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;div&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;class&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;RatingSlider&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;claritySlider&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb96451&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basically there&#039;s an input, and a single slider per input.  The slider&#039;s name is {input name}Slider.  I had some trouble reconciling this setup with the way that the example on the jQuery site is laid out:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb77969&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$(&amp;quot;#slider&amp;quot;).slider({&lt;br /&gt;&amp;nbsp; &amp;nbsp; value:100,&lt;br /&gt;&amp;nbsp; &amp;nbsp; min: 0,&lt;br /&gt;&amp;nbsp; &amp;nbsp; max: 500,&lt;br /&gt;&amp;nbsp; &amp;nbsp; step: 50,&lt;br /&gt;&amp;nbsp; &amp;nbsp; slide: function(event, ui) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $(&amp;quot;#amount&amp;quot;).val(&#039;$&#039; + ui.value);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;});&lt;br /&gt;$(&amp;quot;#amount&amp;quot;).val(&#039;$&#039; + $(&amp;quot;#slider&amp;quot;).slider(&amp;quot;value&amp;quot;));&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb8586&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My problem was, I couldn&#039;t find a way to derive the underlying element&#039;s id from the &quot;ui&quot; parameter in the callback specified for &quot;slide&quot;.  The way I ended up handling this was using the jquery&#039;s .each function to configure the sliders one by one.  Its&#039; not ideal, but its&#039; also not that much more code, and I doubt many pages I&#039;ll be working on have so many sliders that it will become an issue.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb45179&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$(&#039;.RatingSlider&#039;).each(function(idx, elm) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var name = elm.id.replace(&#039;Slider&#039;, &#039;&#039;);&lt;br /&gt;&amp;nbsp; &amp;nbsp; $(&#039;#&#039; + elm.id).slider({&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; value: 3,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; min: 0,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max: 5,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; step: .5,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; slide: function(event, ui) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $(&#039;#&#039; + name).val(ui.value);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; });&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb77109&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I&#039;d be very interested in finding a way to avoid the .each, but I couldn&#039;t find anything on the interwebs and this seems to be working for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;edit:&lt;/strong&gt; I threw together a quick demo per requests in the comments.  You can download it here: &lt;a href=&quot;http://blogs.lessthandot.com/media/blogs/WebDev/using-multiple-jquery-ui-sliders-on-a-si/Sliders.zip&quot; title=&quot;jQuery UI Multiple Slider Demo&quot;&gt;jQuery UI Multiple Slider Demo&lt;/a&gt;.  Hope it helps!&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/using-multiple-jquery-ui-sliders-on-a-si&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>I had a bit of frustration lately when dealing with jQuery UI's slider widget, more specifically when trying to deal with many on the same page.  I wanted to follow the same pattern shown in the example for <a href="http://jqueryui.com/demos/slider/#steps">snap to increments</a> where I have a div for the slider, and the value is actually set to a text input for easy form submission.  The individual elements look something like this:</p>

<div class="codebox"><div class="codeheader"><span>html</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb64278'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb64278','cb47065'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="html" id="cb64278" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><a href="http://december.com/html/4/element/div.html"><span style="color: #000000; font-weight: bold;">&lt;div</span></a> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;SliderControl&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/label.html"><span style="color: #000000; font-weight: bold;">&lt;label</span></a> <span style="color: #000066;">for</span>=<span style="color: #ff0000;">&quot;clarity&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>Clarity:<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/label&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/input.html"><span style="color: #000000; font-weight: bold;">&lt;input</span></a> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;SliderText&quot;</span> <span style="color: #000066;">readonly</span>=<span style="color: #ff0000;">&quot;readonly&quot;</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;clarity&quot;</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;clarity&quot;</span>/<span style="color: #000000; font-weight: bold;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/div.html"><span style="color: #000000; font-weight: bold;">&lt;div</span></a> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;RatingSlider&quot;</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;claritySlider&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/div&gt;</span></span>&nbsp;</li><li style="" class="li1"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/div&gt;</span></span></li></ol></div><div id="cb47065" style="display: none; color: red;"></div></div></div>

<p>Basically there's an input, and a single slider per input.  The slider's name is {input name}Slider.  I had some trouble reconciling this setup with the way that the example on the jQuery site is laid out:</p>

<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb78858'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb78858','cb94461'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb78858" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$(&quot;#slider&quot;).slider({</li><li style="" class="li2">&nbsp; &nbsp; value:100,</li><li style="" class="li1">&nbsp; &nbsp; min: 0,</li><li style="" class="li2">&nbsp; &nbsp; max: 500,</li><li style="" class="li1">&nbsp; &nbsp; step: 50,</li><li style="" class="li2">&nbsp; &nbsp; slide: function(event, ui) {</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; $(&quot;#amount&quot;).val('$' + ui.value);</li><li style="" class="li2">&nbsp; &nbsp; }</li><li style="" class="li1">});</li><li style="" class="li2">$(&quot;#amount&quot;).val('$' + $(&quot;#slider&quot;).slider(&quot;value&quot;));</li></ol></div><div id="cb94461" style="display: none; color: red;"></div></div></div>

<p>My problem was, I couldn't find a way to derive the underlying element's id from the "ui" parameter in the callback specified for "slide".  The way I ended up handling this was using the jquery's .each function to configure the sliders one by one.  Its' not ideal, but its' also not that much more code, and I doubt many pages I'll be working on have so many sliders that it will become an issue.</p>

<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb78511'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb78511','cb59542'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb78511" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$('.RatingSlider').each(function(idx, elm) {</li><li style="" class="li2">&nbsp; &nbsp; var name = elm.id.replace('Slider', '');</li><li style="" class="li1">&nbsp; &nbsp; $('#' + elm.id).slider({</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; value: 3,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; min: 0,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; max: 5,</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; step: .5,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; slide: function(event, ui) {</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $('#' + name).val(ui.value);</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; }</li><li style="" class="li1">&nbsp; &nbsp; });</li><li style="" class="li2">});</li></ol></div><div id="cb59542" style="display: none; color: red;"></div></div></div>

<p>I'd be very interested in finding a way to avoid the .each, but I couldn't find anything on the interwebs and this seems to be working for now.</p>

<p><strong>edit:</strong> I threw together a quick demo per requests in the comments.  You can download it here: <a href="http://blogs.lessthandot.com/media/blogs/WebDev/using-multiple-jquery-ui-sliders-on-a-si/Sliders.zip" title="jQuery UI Multiple Slider Demo">jQuery UI Multiple Slider Demo</a>.  Hope it helps!</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/using-multiple-jquery-ui-sliders-on-a-si">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/using-multiple-jquery-ui-sliders-on-a-si#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=870</wfw:commentRss>
		</item>
				<item>
			<title>Twisting My Arm - How I was Persuaded to Change StructureMap Versions</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/my-compelling-reason-for-upgrading-struc</link>
			<pubDate>Wed, 02 Jun 2010 10:00:00 +0000</pubDate>			<dc:creator>Alex Ullrich</dc:creator>
			<category domain="main">Server Programming</category>
<category domain="alt">ASP.NET</category>			<guid isPermaLink="false">858@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;I&#039;ve been using StructureMap 2.5x for some time, and I have been quite pleased with it.  I&#039;d read a bit about 2.6x, and the improvements to the registry DSL seemed cool but not quite cool enough to give me the motivation I needed to make upgrading the version used in my project a priority.&lt;/p&gt;

&lt;p&gt;A week or two ago, I found what proved to be my compelling reason.  It&#039;s a method called&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb11374&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;ObjectFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;ReleaseAndDisposeAllHttpScopedObjects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb34256&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it does just what it says it does.  This could really help in one particular situation encountered in my application (using StructureMap for NHibernate session management in a web context).  I need to cache the session factory as a singleton, but the sessions themselves should be unique &lt;em&gt;per request&lt;/em&gt;.  Before coming across this method I was using a filter to clean up sessions at the end of the request:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb56431&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; SessionPerRequest : ActionFilterAttribute&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; OnActionExecuted &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ActionExecutedContext filterContext&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;// for when session doesn&#039;t need to remain open for view rendering&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;//StructureMap.ObjectFactory.GetInstance&amp;lt;NHibernate.ISession&amp;gt;().Dispose();&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;override&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; OnResultExecuted &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ResultExecutedContext filterContext&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; StructureMap.&lt;span style=&quot;color: #0000FF;&quot;&gt;ObjectFactory&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;NHibernate.&lt;span style=&quot;color: #0000FF;&quot;&gt;ISession&lt;/span&gt;&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Dispose&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb70238&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was working fine, but I did need to decorate every controller method where I wanted to apply it. In addition to not really being my idea of a good time, this leaves the door open for me to forget to decorate a method somwhere, causing the app to leak sessions all over the place.  Also, if I put it someplace it&#039;s not needed, a session will be created only to be disposed of!&lt;/p&gt;

&lt;p&gt;So today, I finally got around to upgrading.  I made the slight improvements to Registry code that were now possible, but I was most excited to get rid of the filter above, and replace it with this method (in Global.asax.cs):&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb18331&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Application_EndRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; ObjectFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;ReleaseAndDisposeAllHttpScopedObjects&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb64239&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While there was certainly some pain involved in removing the filter I had used previously, this approach will make my life much easier in the long run.  I already trust StructureMap to cache these different instances for me correctly, why not trust it to clean up afterwards as well?  It&#039;s also nice to know that if I end up needing any other objects to be unique per http context, I won&#039;t need to write any more code to clean up after myself.  This is a great feature added to the library, and I wish I&#039;d read about it sooner!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; I saw a thread today mentioning another method called &lt;/p&gt;&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb90946&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;HttpContextBuildPolicy.&lt;span style=&quot;color: #0000FF;&quot;&gt;DisposeAndClearAll&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb67453&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt; that&#039;s been available since StructureMap 2.3.5 that seems to do the exact same thing.  So I didn&#039;t really need to upgrade after all, but I guess its nice to stay current.  I don&#039;t want to make excuses for my ignorance, but I&#039;ve gotta say accessing it through the ObjectFactory makes a bit more sense to me.  In a way I&#039;m more impressed that it showed up there, because its&#039; often a harder decision to relocate code when refactoring than to add it in the first place!&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/my-compelling-reason-for-upgrading-struc&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>I've been using StructureMap 2.5x for some time, and I have been quite pleased with it.  I'd read a bit about 2.6x, and the improvements to the registry DSL seemed cool but not quite cool enough to give me the motivation I needed to make upgrading the version used in my project a priority.</p>

<p>A week or two ago, I found what proved to be my compelling reason.  It's a method called</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb15442'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb15442','cb58681'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb15442" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">ObjectFactory.<span style="color: #0000FF;">ReleaseAndDisposeAllHttpScopedObjects</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb58681" style="display: none; color: red;"></div></div></div>

<p>And it does just what it says it does.  This could really help in one particular situation encountered in my application (using StructureMap for NHibernate session management in a web context).  I need to cache the session factory as a singleton, but the sessions themselves should be unique <em>per request</em>.  Before coming across this method I was using a filter to clean up sessions at the end of the request:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb27753'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb27753','cb34141'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb27753" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> SessionPerRequest : ActionFilterAttribute</li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">override</span> <span style="color: #0600FF;">void</span> OnActionExecuted <span style="color: #000000;">&#40;</span>ActionExecutedContext filterContext<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008080; font-style: italic;">// for when session doesn't need to remain open for view rendering</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008080; font-style: italic;">//StructureMap.ObjectFactory.GetInstance&lt;NHibernate.ISession&gt;().Dispose();</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">override</span> <span style="color: #0600FF;">void</span> OnResultExecuted <span style="color: #000000;">&#40;</span>ResultExecutedContext filterContext<span style="color: #000000;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; StructureMap.<span style="color: #0000FF;">ObjectFactory</span>.<span style="color: #0000FF;">GetInstance</span>&lt;NHibernate.<span style="color: #0000FF;">ISession</span>&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Dispose</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb34141" style="display: none; color: red;"></div></div></div>

<p>This was working fine, but I did need to decorate every controller method where I wanted to apply it. In addition to not really being my idea of a good time, this leaves the door open for me to forget to decorate a method somwhere, causing the app to leak sessions all over the place.  Also, if I put it someplace it's not needed, a session will be created only to be disposed of!</p>

<p>So today, I finally got around to upgrading.  I made the slight improvements to Registry code that were now possible, but I was most excited to get rid of the filter above, and replace it with this method (in Global.asax.cs):</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb13305'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb13305','cb4410'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb13305" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">void</span> Application_EndRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li2"><span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; ObjectFactory.<span style="color: #0000FF;">ReleaseAndDisposeAllHttpScopedObjects</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb4410" style="display: none; color: red;"></div></div></div>

<p>While there was certainly some pain involved in removing the filter I had used previously, this approach will make my life much easier in the long run.  I already trust StructureMap to cache these different instances for me correctly, why not trust it to clean up afterwards as well?  It's also nice to know that if I end up needing any other objects to be unique per http context, I won't need to write any more code to clean up after myself.  This is a great feature added to the library, and I wish I'd read about it sooner!</p>

<p><strong>EDIT:</strong> I saw a thread today mentioning another method called </p><div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb93372'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb93372','cb9334'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb93372" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">HttpContextBuildPolicy.<span style="color: #0000FF;">DisposeAndClearAll</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li></ol></div><div id="cb9334" style="display: none; color: red;"></div></div></div><p> that's been available since StructureMap 2.3.5 that seems to do the exact same thing.  So I didn't really need to upgrade after all, but I guess its nice to stay current.  I don't want to make excuses for my ignorance, but I've gotta say accessing it through the ObjectFactory makes a bit more sense to me.  In a way I'm more impressed that it showed up there, because its' often a harder decision to relocate code when refactoring than to add it in the first place!</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/my-compelling-reason-for-upgrading-struc">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/my-compelling-reason-for-upgrading-struc#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=858</wfw:commentRss>
		</item>
			</channel>
</rss>
