<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" version="2.0">

<channel>
	<title>Where's Lou</title>
	
	<link>http://whereslou.com</link>
	<description>It works on my machine</description>
	<pubDate>Wed, 08 Jul 2009 14:30:55 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/WheresLou" type="application/rss+xml" /><item>
		<title>Retrofit ASP.NET Aspx Urls with System.Web.Routing</title>
		<link>http://whereslou.com/2009/07/08/retrofit-aspnet-aspx-urls-with-systemwebrouting</link>
		<comments>http://whereslou.com/2009/07/08/retrofit-aspnet-aspx-urls-with-systemwebrouting#comments</comments>
		<pubDate>Wed, 08 Jul 2009 14:30:55 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[tech]]></category>

		<category><![CDATA[asp.net]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[routing]]></category>

		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=600</guid>
		<description><![CDATA[Here&#8217;s a sample of a technique that might be of general interest. It came up retrofitting a particular url structure onto an existing ASP.NET application originally built with the Web Client Software Factory. I came into the project fairly late in the game and the ROI for migrating to MVC would be nearly zero, so [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a sample of a technique that might be of general interest. It came up retrofitting a particular url structure onto an existing ASP.NET application originally built with the <a href="http://websf.codeplex.com/">Web Client Software Factory</a>. I came into the project fairly late in the game and the ROI for migrating to MVC would be nearly zero, so instead a technique was used which welds System.Web.Routing onto a web forms app in a fairly transparent fashion.</p>
<div id="attachment_603" class="wp-caption alignright" style="width: 220px"><img src="http://whereslou.com/wp-content/uploads/2009/07/lipstick-210x300.jpg" alt="Putting Lipstick on a Web Site" title="lipstick" width="210" height="300" class="size-medium wp-image-603" /><p class="wp-caption-text">Putting Lipstick on a Web Site</p></div>
<p>To start with there are a few ground rules and goals to establish. One is that a 301 permanent redirect is desirable from older urls onto new routed urls. Another is that not all urls in the app need to be fixed &#8220;in place&#8221; in the original html response assuming the 301 redirects will keep the resulting urls canonical. Third is that it&#8217;s highly desirable to be able to calculate the new route given an old url server-side, for such things as the site map and updating the links on pages which are particularly significant.<br />
<span id="more-600"></span><br />
Another consideration is that if a site is currently using Request.QueryString values and relies on postback, both of those should continue to work transparently. A final consideration is the range of possible values in a piece of the route could be driven by a database or source which is non-static or has a huge number of possible distinct values. At least a number too large for adding multiple routes or combining into regex restriction.</p>
<p>Downloads: <a href='http://whereslou.com/wp-content/uploads/2009/07/retrofit.zip'>retrofit.zip</a> source code and sample project.</p>
<p>It turns out the System.Web.Routing module is particularly well suited for this, especially because of the bidirectional nature of it&#8217;s routes. The attached solution has a sample web app and a class library named Retrofit.Routing which provides an example of using System.Web.Routing in this way.</p>
<p>First of all is the class Retrofit.Routing.WebFormsRouteHandler which implements IRouteHandler. You&#8217;ll find in it&#8217;s implementation the method GetHttpHandler which will use BuildManager.CreateInstanceFromVirtualPath to obtain an instance of the targetted web page. The web page is targetted with the RouteData Value parameter named &#8220;VirtualPath&#8221;, much like in an MVC app the controller is targetted with a RouteData Value named &#8220;Controller&#8221;. The other thing this class does is call HttpContext.RewritePath to extend the number of values present on the QueryString. </p>
<p>You won&#8217;t use the WebFormsRouteHandler directly, instead you&#8217;ll create an instance of WebFormsRoute. This WebFormsRoute is a descendant of the standard Route class and is used in almost exactly the same way.</p>
<pre class="brush: csharp;">

public static void RegisterRoutes(RouteCollection routes)
{
    // enable routing of existing files so WebFormsRoute has the chance redirect old urls
    routes.RouteExistingFiles = true;

    // matches customer with a data-driven constraint
    routes.Add(new WebFormsRoute(
        &quot;Customer/{*CompanyName}&quot;,
        new RouteValueDictionary(new {VirtualPath = &quot;~/Contacts/Customer.aspx&quot;}),
        new RouteValueDictionary(new {CompanyName = new ValidCompanyNameConstraint()})
    ));

    // matches orders requests
    routes.Add(new WebFormsRoute(
        &quot;Customer/Orders/{OrderID}&quot;,
        new RouteValueDictionary(new {VirtualPath = &quot;~/Orders/Details.aspx&quot;})
    ));

}
</pre>
<p>As we can see defaults can be provided, which is how the value for VirtualPath is defaulted in when a request pattern is matched. The values like {CompanyName} and {OrderID} which are matched above will become CompanyName= and OrderID= querystring parameters passed to the aspx page as it runs, so the code behind does not need to be modified for those cases.</p>
<p>There&#8217;s even an example above of how you would provide an IRouteConstraint class if you would like to dynamically restrict the set of urls a route is allowed to match.</p>
<pre class="brush: csharp;">
public class ValidCompanyNameConstraint : IRouteConstraint
{
    public ValidCompanyNameConstraint()
    {
        // pre-load valid company names
        // in a real app this would be queries each call to match,
        // or it would be re-cached when the data had changed.

        var data = new NorthwindDataContext();
        CachedNames = data.Customers.ToDictionary(x =&gt; x.CompanyName, x =&gt; x.CustomerID);
    }

    private Dictionary&lt;string, string&gt; CachedNames { get; set; }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        var parameterValue = Convert.ToString(values[parameterName]);
        return CachedNames.ContainsKey(parameterValue);
    }
}
</pre>
<p>It&#8217;s a contrived example, but it is just a sample after all.</p>
<p>And the WebFormsRoute is performing one other task related to the need for 301 redirects. As requests are coming in it&#8217;s not only matching the request against the new url pattern, it&#8217;s also testing the request url against the old virtual path and query string parameters needed. </p>
<pre class="brush: csharp;">
    // do the work to check if the incoming request could also produce
    // a meaningful route to redirect towards
    var probeVirtualPathData = TestVirtualPath(httpContext, virtualPath);
    if (probeVirtualPathData == null)
        return null;

    // the request is on a page which this route controls, 301 redirect
    // onto the correct location
    return new RouteData(this, new PermanentRewriteHandler(probeVirtualPathData.VirtualPath));
}
</pre>
<p>If the incoming request is match for the old url structure then it takes advantage System.Web.Routing&#8217;s ability to calculate the correct url in the new pattern. This new url is used as the parameter of a handler which will return a 301 permanent redirect to keep the browser&#8217;s address bar clean-looking and to ensure web crawlers are indexing the site with at this single, canonical location.</p>
<p>The rewrite handler itself is a pretty simple device.</p>
<pre class="brush: csharp;">
public class PermanentRewriteHandler : IRouteHandler, IHttpHandler
{
    private readonly string _path;

    public PermanentRewriteHandler(string path)
    {
        _path = path;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return this;
    }

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Buffer = true;
        context.Response.Redirect(&quot;~/&quot; + _path, false);
        context.Response.StatusCode = 301;
        context.Response.StatusDescription = &quot;Moved Permanently&quot;;
    }
}
</pre>
<p>At this point nearly all of the goals are met. You can route incoming requests on new urls onto existing WebForms pages, it will 301 redirect incoming old urls onto the new location, the route values are converted to querystring values transparently, and POST to the WebForms life-cycle will pass through unimpeded on the old or new urls.</p>
<p>The final piece of the puzzle is the need to return the new urls in links returned from pages on the server without relying on the 301 redirects. That&#8217;s done easily enough with an extension method Page.ResolveRouteUrl that is used in the same way as Page.ResolveUrl.</p>
<pre class="brush: csharp;">
public static string ResolveRouteUrl(this Page page, string relativeUrl)
{
    var url = relativeUrl;

    var virtualPathData = GetVirtualPathData(relativeUrl);
    if (virtualPathData != null)
    {
        url = &quot;~/&quot; + virtualPathData.VirtualPath;
    }

    return page.ResolveUrl(url);
}
</pre>
<p>And it&#8217;s used like this:</p>
<pre class="brush: xml;">
Company: &lt;asp:HyperLink ID=&quot;HyperLink1&quot; runat=&quot;server&quot;
                NavigateUrl='&lt;%# Page.ResolveRouteUrl(&quot;~/Contacts/Customer.aspx?CompanyName=&quot; + Eval(&quot;CompanyName&quot;)) %&gt;' Text='&lt;%# Eval(&quot;CompanyName&quot;) %&gt;'&gt;&lt;/asp:HyperLink&gt;
</pre>
<p>Downloads: <a href='http://whereslou.com/wp-content/uploads/2009/07/retrofit.zip'>retrofit.zip</a> source code and sample project.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/iZn1xp_Odgo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/07/08/retrofit-aspnet-aspx-urls-with-systemwebrouting/feed</wfw:commentRss>
		</item>
		<item>
		<title>Camping weekend</title>
		<link>http://whereslou.com/2009/07/05/camping-weekend</link>
		<comments>http://whereslou.com/2009/07/05/camping-weekend#comments</comments>
		<pubDate>Mon, 06 Jul 2009 03:55:51 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[family]]></category>

		<category><![CDATA[camping]]></category>

		<category><![CDATA[fireworks]]></category>

		<category><![CDATA[spam]]></category>

		<category><![CDATA[vacation]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=593</guid>
		<description><![CDATA[Lots of fun this weekend camping with Brenda. It was a scramble with some light grocery shopping and packing beforehand so some of the food was improvised. One thing which turned out surprisingly well was the Spam and cheese sandwiches.
In a nutshell - duct tape a tongs to the end of a spatula for length. [...]]]></description>
			<content:encoded><![CDATA[<p>Lots of fun this weekend camping with Brenda. It was a scramble with some light grocery shopping and packing beforehand so some of the food was improvised. One thing which turned out surprisingly well was the Spam and cheese sandwiches.</p>
<p><a href="http://www.spam.com/"><img src="http://whereslou.com/wp-content/uploads/2009/07/spam-150x150.jpg" alt="spam" title="spam" width="150" height="150" class="alignright size-medium wp-image-594" /></a>In a nutshell - duct tape a tongs to the end of a spatula for length. Use the spatula to slice spam into 8mm slices. Place single slice on spatula and wrap around with tin foil, twisting like hard candy wrapper. Cook liberally over fire, unwrap carefully, put on hamburger bun and spray with pressurized cheese in a can. Enjoy.</p>
<p>Brenda swore by them. A warning however - have only one without an antacid on hand.</p>
<p><img src="http://whereslou.com/wp-content/uploads/2009/07/fireworks3-225x300.jpg" alt="fireworks3" title="fireworks3" width="225" height="300" class="alignleft size-medium wp-image-595" />There was a brilliant fireworks display on the fourth with dozens of cakes of two- and three-inch mortars. Long story short Brenda&#8217;s cousin was involved in the show and I was enlisted to be perimeter security. So with flashlights in hand as Brenda and I watched for anyone who would may have wandered up to the launching area and we had point blank view of the show. Didn&#8217;t get hit by much debris at all. :)</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/dKGyD4i-vfs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/07/05/camping-weekend/feed</wfw:commentRss>
		</item>
		<item>
		<title>Hooking up The Lounge - seems like a perfect fit</title>
		<link>http://whereslou.com/2009/06/29/hooking-up-the-lounge-seems-like-a-perfect-fit</link>
		<comments>http://whereslou.com/2009/06/29/hooking-up-the-lounge-seems-like-a-perfect-fit#comments</comments>
		<pubDate>Tue, 30 Jun 2009 02:55:13 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[advertising]]></category>

		<category><![CDATA[thelounge]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=588</guid>
		<description><![CDATA[
I was noticing the traffic on the site for Spark project information and documentation has been picking up nicely. That lead me to thinking about bumping up to another notch on the slice size, but that sort of doubles the hosting cost. It&#8217;s pretty modest figure but it can add up over the year.
With the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://theloungenet.com/"><img src="http://whereslou.com/wp-content/uploads/2009/06/thelounge-300x78.png" alt="The Lounge" title="The Lounge" width="300" height="78" class="alignright size-medium wp-image-589" /></a></p>
<p>I was noticing the traffic on the site for Spark project information and documentation has been picking up nicely. That lead me to thinking about bumping up to another notch on the slice size, but that sort of doubles the hosting cost. It&#8217;s pretty modest figure but it can add up over the year.</p>
<p>With the hosting costs at the top of my mind I noticed that a number of the blogs I&#8217;ve been reading like <a href="http://haacked.com">Haack</a>&#8217;s, <a href="http://hanselman.com">Hanselman</a>&#8217;s, and <a href="http://blog.donnfelker.com">Felker</a>&#8217;s are showing ads from <a href="http://thelounge.com">The Lounge</a>. The ads seemed pretty topical too, relating to a tight cluster of dotnet development and web hosting subject matter, so I dropped them a line to see if the volume of traffic I had was on their radar.</p>
<p>Turns out they do have an advertising bundle of open source product sites, like <a href="http://ninject.org/">Ninject</a>, which <a href="http://sparkviewengine.com">Spark</a> fit into quite nicely. One day later everything&#8217;s all hooked up and running.</p>
<p>It&#8217;s nice to have a little splash of color on the page too. I didn&#8217;t realize it at the time (paging through all of the Drupal themes of all shape and color) but that particular theme is pretty stark on pages where the blue info block isn&#8217;t present.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/rgZ6Wsv5myk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/06/29/hooking-up-the-lounge-seems-like-a-perfect-fit/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spark v1.0 and presentation for MPLS ALT.NET</title>
		<link>http://whereslou.com/2009/06/24/spark-releas-and-presentation-for-mpls-altnet</link>
		<comments>http://whereslou.com/2009/06/24/spark-releas-and-presentation-for-mpls-altnet#comments</comments>
		<pubDate>Wed, 24 Jun 2009 23:23:07 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[spark]]></category>

		<category><![CDATA[alt.net]]></category>

		<category><![CDATA[aspnetmvc]]></category>

		<category><![CDATA[presentation]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=555</guid>
		<description><![CDATA[The first news is that the Spark v1.0 bits are now officially released and are the default download at CodePlex. It re-opens the gates to more substantial changes that have been postponed for the release milestone.
 Coinciding with the release I did a presentation of Spark on the 18th for the Minneapolis ALT.NET group organized [...]]]></description>
			<content:encoded><![CDATA[<p>The first news is that the <a href="http://sparkviewengine.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=27600">Spark v1.0</a> bits are now officially released and are the default download at CodePlex. It re-opens the gates to more substantial changes that have been postponed for the release milestone.</p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/slide1.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide1-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignright size-medium wp-image-556" /></a> Coinciding with the release I did a presentation of <a href="http://sparkviewengine.com">Spark</a> on the 18th for the <a href="http://mplsalt.net">Minneapolis ALT.NET</a> group organized by <a href="http://freemind.net/">Freemind</a>.</p>
<p>I had mentioned I was preparing for this and someone on Twitter asked if it would be recorded, so I arranged for a camcorder to be present.</p>
<blockquote style="margin-top:0px;margin-bottom:0px;"><p>
Downloadable materials:<br />
<a href='http://whereslou.com/wp-content/uploads/2009/06/spark-view-engine.pptx'>PowerPoint slides</a><br />
<a href='http://whereslou.com/wp-content/uploads/2009/06/MplsAltNet.2009-06-18.zip'>All files and projects</a><br />
<strong>New</strong> <a href="/download/video:3995785?v=2&#038;e=1246135321&#038;h=7c4ec4e5b09b7d2f8c36256b594b00b6&#038;uh=69d331475fa4ebb28b6bd057963527be">Downloadable version</a> at 1024&#215;768, 982.21MB
</p></blockquote>
<p><object width="640" height="480"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=5306909&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=5306909&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="640" height="480"></embed></object></p>
<p> <a href="http://whereslou.com/wp-content/uploads/2009/06/slide2.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide2-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a> <a href="http://whereslou.com/wp-content/uploads/2009/06/slide3.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide3-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/slide4.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide4-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a> <a href="http://whereslou.com/wp-content/uploads/2009/06/slide5.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide5-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/slide6.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide6-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a> <a href="http://whereslou.com/wp-content/uploads/2009/06/slide7.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide7-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/slide8.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide8-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a> <a href="http://whereslou.com/wp-content/uploads/2009/06/slide9.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/slide9-300x225.png" alt="Spark View Engine" title="Spark View Engine" width="300" height="225" class="alignnone size-medium wp-image-556" /></a></p>
<p>There were some lessons learned. One is that <a href="http://camstudio.org/">CamStudio</a> will record as long as you like, then fail when it&#8217;s compressing and interleaving audio if the result is larger than 4GB.</p>
<p>Fortunately, out of a deep-seated (and apparently justified) distrust of technology there was also a camcorder on a tripod. An <a href="http://avisynth.org/mediawiki/Main_Page">Avisynth</a> filter named <a href="http://avisynth.org/vcmohan/Reform/Reform.html">Reform</a> provided some fairly acceptable results reconstructing a 4:3 aspect ratio of the screen.</p>
<p>There is an additional 30 minutes of audio captured by CamStudio after the tape ran out. I&#8217;m thinking of recapturing a video to go with that.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/cwwLzU68XRg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/06/24/spark-releas-and-presentation-for-mpls-altnet/feed</wfw:commentRss>
		</item>
		<item>
		<title>ViewData Dictionary vs. View typed Model</title>
		<link>http://whereslou.com/2009/06/17/viewdata-dictionary-vs-view-typed-model</link>
		<comments>http://whereslou.com/2009/06/17/viewdata-dictionary-vs-view-typed-model#comments</comments>
		<pubDate>Wed, 17 Jun 2009 22:04:23 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[aspnetmvc]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[suggestion]]></category>

		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=542</guid>
		<description><![CDATA[This post exists because trying to explain something 140 characters at a time makes you sound insane.
Earlier Brad Wilson speculated:
 Theory: in ASP.NET MVC, master pages cause users to use ViewData instead of strongly typed models more often than any other reason.


To which I replied:
 @bradwilson That, plus keeping properties in sync on a class [...]]]></description>
			<content:encoded><![CDATA[<p>This post exists because trying to explain something 140 characters at a time makes you sound insane.</p>
<p>Earlier <a href="http://bradwilson.typepad.com/">Brad Wilson</a> speculated:</p>
<blockquote><p><a href="http://twitter.com/bradwilson"><img src="http://s3.amazonaws.com/twitter_production/profile_images/70245673/Brad__transparent__bigger.png" style="float:left;"/></a> Theory: in ASP.NET MVC, master pages cause users to use ViewData instead of strongly typed models more often than any other reason.
</p></blockquote>
<div style="clear:both;"></div>
<p>To which I replied:</p>
<blockquote><p><a href="http://twitter.com/loudej"><img src="http://s3.amazonaws.com/twitter_production/profile_images/22441252/DSC01367_bigger.jpg" style="float:left;"/></a> <a href="http://twitter.com/bradwilson">@bradwilson</a> That, plus keeping properties in sync on a class that exists only to stand between an action and view isn&#8217;t very DRY
</p></blockquote>
<div style="clear:both;"></div>
<p>It&#8217;s true there are a huge number of reasons to need data in a master layout like navigation, sidebar widgets, current user profile, ad codes, etc. But if that was the main cause should be pretty easy to address by having the master layout&#8217;s data in a layer superclass for all of your action-view models.</p>
<p>I believe another even more serious problem is the view model can make you repeat yourself a huge number of times.</p>
<p>One of the things that&#8217;s really nice about actions on a controller is you can avoid the proliferation of classes you get with the command pattern. If you make a model for each action you get that problem back in spades. You could keep track of commonality between actions to reduce the number of action/view model classes, but that&#8217;s more work and you&#8217;re playing a balancing act between how many classes you are making and how many unused properties are acceptable for actions that don&#8217;t produce strictly identical data.</p>
<p>Plus it&#8217;s not DRY&#8230; I&#8217;ve added the action to a controller, then I need to add the class to hold the data. I&#8217;ve acquired some data in the action, then I need to add the typed property to the action&#8217;s model. Some data&#8217;s refactored out of the action, then I need to remember to remove it from the data bucket class too.</p>
<p>Just to throw an idea out there, to have a DRY action-view model class, how about adding the idea of a dynamic partial class to the language? A partial class which merges with additional auto-implemented properties at compile-time, as a result of the <em>property assignments</em> on that type.</p>
<p>So here&#8217;s that you would have today. The following assumes DefaultMasterData members will be assigned by filters if the model has an appropriate base class.</p>
<pre class="brush: csharp;">

public class DefaultMasterData
{
	public UserProfile Profile {get;set;}
	public string AdvertisementCampaignCode {get;set;}
}

public class HomeIndexData : DefaultMasterData
{
    public Product Product {get;set;}
	public Order CurrentOrder {get;set;}
}

[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
    public ActionResult Index()
    {
		...acquiring stuff here...

		// return view of model
	    return View(new HomeIndexData {Product = mostRecentProduct, CurrentOrder = activeSessionOrder});
    }
}
</pre>
<p>Here&#8217;s if you had a dynamic partial class. The assignment of non-existant properties results in an implicit partial with auto-implemented properties to be merged in at compile time.</p>
<pre class="brush: csharp;">

public class DefaultMasterData
{
	public UserProfile Profile {get;set;}
	public string AdvertisementCampaignCode {get;set;}
}

public dynamic partial class HomeIndexData : DefaultMasterData
{
}

[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
    public ActionResult Index()
    {
		...acquiring stuff here...

		// return view of model
		// creates implicit partial with auto-implemented properties
	    return View(new HomeIndexData {Product = mostRecentProduct, CurrentOrder = activeSessionOrder});
    }
}
</pre>
<p>And to kill just a bit more code, how about a the ability to declare the named type without the empty class statement?</p>
<pre class="brush: csharp;">

public class DefaultMasterData
{
	public UserProfile Profile {get;set;}
	public string AdvertisementCampaignCode {get;set;}
}

[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
    public ActionResult Index()
    {
		...acquiring stuff here...

		// return view of model
		// creates implicit partial with auto-implemented properties
		// also results in the initial declaration of the type HomeIndexData based on DefaultMasterData
	    return View(new dynamic partial HomeIndexData:DefaultMasterData
	        {Product = mostRecentProduct, CurrentOrder = activeSessionOrder}
	    );
    }
}
</pre>
<p>There! Now I think you&#8217;ll see people using a base of ViewPage&lt;HomeIndexData&gt; in Index.aspx.</p>
<p>Clearly there are lots of questions about namespace, etc., but you get the idea.</p>
<hr/>
<p>Final note, to declare an anonymous type with a base class, it would be nice to support something like.</p>
<pre class="brush: csharp;">
var x = new :MyBase {Foo=&quot;bar&quot;, Frap=5};
</pre>
<hr/>
<p>Another final note - you wouldn&#8217;t need to do it all inline of course.</p>
<pre class="brush: csharp;">

[UserProfileFilter]
[AdvertisingFilter]
public class HomeController
{
    public ActionResult Index()
    {
	    var model = new dynamic partial HomeIndexData:DefaultMasterData();

		...acquiring stuff here...

		// assignments causing more auto-impl props here
	    model.Product = mostRecentProduct;
	    model.CurrentOrder = activeSessionOrder;

		// return view of model
	    return View(model);
    }
}
</pre>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/e3KCGlzy3ec" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/06/17/viewdata-dictionary-vs-view-typed-model/feed</wfw:commentRss>
		</item>
		<item>
		<title>Using Live Mesh and Git - the best of both worlds</title>
		<link>http://whereslou.com/2009/06/04/using-live-mesh-and-git-the-best-of-both-worlds</link>
		<comments>http://whereslou.com/2009/06/04/using-live-mesh-and-git-the-best-of-both-worlds#comments</comments>
		<pubDate>Thu, 04 Jun 2009 07:35:05 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[tech]]></category>

		<category><![CDATA[git]]></category>

		<category><![CDATA[live mesh]]></category>

		<category><![CDATA[microsoft]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=506</guid>
		<description><![CDATA[I like Live Mesh. I like Git, too.
If you&#8217;ve used a folder synchronizing tool before that will propogates file changes between machines, like FolderShare or DropBox, there&#8217;s a good chance you&#8217;ve had a scare with some possible data loss. Maybe you accidentally dropped a folder into a subfolder, &#8220;made space&#8221; by getting rid of files [...]]]></description>
			<content:encoded><![CDATA[<p>I like Live Mesh. I like Git, too.</p>
<p>If you&#8217;ve used a folder synchronizing tool before that will propogates file changes between machines, like <a href="http://www.foldershare.com/">FolderShare</a> or <a href="http://www.getdropbox.com/">DropBox</a>, there&#8217;s a good chance you&#8217;ve had a scare with some possible data loss. Maybe you accidentally dropped a folder into a subfolder, &#8220;made space&#8221; by getting rid of files on one of the machines, or the sync software went crazy. Whatever the case is the change is immediately shipped onto all of your backups all over the world. </p>
<p>That effect makes file sync tools more of a convenience than a backup strategy - it can lower your mean time to recovery in case of hardware failure - but even though there are two or more physical copies of your bits there&#8217;s really one logical copy that appears in several places where it can be accidentally deleted. Ironically the convenience of having your data everywhere also lowers it&#8217;s mean-time-to-accidental-destruction.</p>
<p>Which is exactly the problem a change control repository like Git is designed to solve! Fearing data loss as much as the next person, I tried to combine the convenience of Live Mesh with the safety of Git and it worked really well. Here&#8217;s how it went, assuming you have <a href="http://code.google.com/p/msysgit/downloads/list">msysGit</a> and <a href="https://www.mesh.com/welcome/default.aspx">Live Mesh</a> installed on your machine.<br />
<span id="more-506"></span></p>
<p><ins datetime="2009-06-05T06:31:20+00:00"><strong>Update</strong> here&#8217;s a diagram of what we&#8217;re ultimately going to try to accomplish.</ins></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/livemesh.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/livemesh.png" alt="livemesh" title="livemesh" width="674" height="340" class="alignnone size-full wp-image-537" /></a></p>
<p><strong>Initialize Git</strong></p>
<p>First off - the goal is <em>not</em> to put a Git repository into a Live Mesh folder. Git already has a push-pull mechanism to move changes around and the word is Live Mesh will just trash your repos files by moving overlapping changes over each other on different nodes. Instread what we&#8217;ll be doing is creating a new Git repository and using a Live Mesh folder as it&#8217;s working copy.</p>
<pre class="brush: text;">
:create a new folder to hold files
mkdir &quot;%userprofile%\Documents\Junk Drawer&quot;

:go to it
cd &quot;%userprofile%\Documents\Junk Drawer&quot;

:initialize a repository
git init

:mark the .git subfolder hidden
attrib +H .git
</pre>
<p>Or if you prefer to avoid command-line you can do the same thing with Git GUI and Explorer.</p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/git-gui-here.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/git-gui-here-300x229.png" alt="git-gui-here" title="git-gui-here" width="300" height="229" class="alignnone size-medium wp-image-514" /></a></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/create-new-repository.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/create-new-repository-300x142.png" alt="create-new-repository" title="create-new-repository" width="300" height="142" class="alignnone size-medium wp-image-515" /></a></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/git-folder-properties.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/git-folder-properties-300x210.png" alt="git-folder-properties" title="git-folder-properties" width="300" height="210" class="alignnone size-medium wp-image-517" /></a></p>
<p><strong>Initialize Live Mesh</strong></p>
<p>Then the magic happens - add the Junk Folder to Live Mesh.</p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/add-folder-to-live-mesh.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/add-folder-to-live-mesh.png" alt="add-folder-to-live-mesh" title="add-folder-to-live-mesh" width="352" height="220" class="alignnone size-full wp-image-522" /></a> <a href="http://whereslou.com/wp-content/uploads/2009/06/add-folder.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/add-folder.png" alt="add-folder" title="add-folder" width="495" height="246" class="alignnone size-full wp-image-523" /></a></p>
<p>Throw some files in there and they&#8217;re automatically synchronized onto Live Desktop online (and to other devices which you have synchronizing the Junk Drawer). The Live Desktop gives you a browser-based Explorer-like interface to your files.</p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/live-desktop.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/live-desktop.png" alt="live-desktop" title="live-desktop" width="356" height="344" class="alignnone size-full wp-image-525" /></a></p>
<p>You can now access and modify the files in the Junk Drawer from any number of devices, including downloading and uploading files through mesh.com. All changes will be synchronized at every location including the one we originally created which is the working directory of the Git repository.</p>
<p><strong>Commit Changes</strong></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/commit-changes.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/commit-changes-300x219.png" alt="commit-changes" title="commit-changes" width="300" height="219" class="alignright size-medium wp-image-527" /></a> Git keeps track of your files in the working folder when you select the changes to stage, provide a comment, and hit commit. Because it&#8217;s all synchronized it doesn&#8217;t matter which device caused the changes. There are plenty of Git tutorials out there so I won&#8217;t go through all the steps of adding files and committing them, looking at history, restoring files from source control. In the end the point is that this Git repository is now providing change control tracking for my Junk Drawer folder and (by extension) all devices on the Mesh hosting that folder even if they don&#8217;t know it.</p>
<p><strong>Push Changes</strong></p>
<p>If we stopped here there would be a history and some protection against accidental deletion (because you can get the files out of Git if they disappear) but there&#8217;s still a risk we&#8217;re exposed to. All of the repository data is saved in the hidden &#8220;Junk Drawer\.git&#8221; folder which could be lost if the drive fails or if the Mesh goes insane and wipes out the entire Junk Drawer parent folder.</p>
<p>Fortunately it&#8217;s a really easy problem to take care of - from this git repository simply initialize a new remote repository and push all changes to it. Okay, maybe it&#8217;s a little tricky to get set up because msys and git are a bit&#8230; awkward&#8230; when it comes to drive letters and such. So here are the commands you&#8217;ll use to create a remote repos on a known UNC.</p>
<pre class="brush: text;">
:map a drive long enough to initialize an empty repository.
:use whatever unc and drive letter is appropriate for you
net use G: \\10.0.0.3\storage

:go into the working folder
cd &quot;%userprofile%\Documents\Junk Drawer&quot;

:and create a bare repository on the file server
:use whatever folder name makes sense
:the foldername.git extension is conventional for Git repositories
:that don't have a working copy of the files checked out
git clone --bare . G:/junk.git

:unmap the drive letter
net use /delete G:

:inform the local repository that &quot;origin&quot; refers to this new unc git location
git remote add origin //10.0.0.3/storage/junk.git

:and finally push over to the &quot;origin&quot; repos everything on the default &quot;master&quot; branch
git push origin master
</pre>
<p>Once the association between the local and remote repos is established you can push to it from Git GUI any time you commit changes on your mesh.</p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/06/push-branches.png"><img src="http://whereslou.com/wp-content/uploads/2009/06/push-branches-292x300.png" alt="push-branches" title="push-branches" width="292" height="300" class="alignnone size-medium wp-image-529" /></a></p>
<p><strong>Conclusion</strong><br />
And there you have it!</p>
<p>* You can have a Live Mesh folder synchronized between any number of devices.<br />
* Each device can create, access, and modify those files normally without being aware of change-control<br />
* You can commit changes on your mesh folder into a Git repos exactly as if it was a working folder<br />
* You can push commits to an even more rugged Git repos on a distinct and isolated storage location</p>
<p>And now your files are safe - even if Live Mesh goes completely insane destroying all of your files <em>and</em> the hidden .git folder inside the original device&#8217;s Live Mesh folder. All you need to do is clone the Git repos down from the storage location back onto your hard drive and you&#8217;ll have all of the changes you pushed earlier in a new local repos and a rebuilt working folder filled with all of your files. Mark the new .git folder as hidden again, add the working folder back into Live Mesh, and you&#8217;re up and running again.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/evjc8GdVYoA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/06/04/using-live-mesh-and-git-the-best-of-both-worlds/feed</wfw:commentRss>
		</item>
		<item>
		<title>Starting with Spark in ten minutes</title>
		<link>http://whereslou.com/2009/05/27/starting-with-spark-in-ten-minutes</link>
		<comments>http://whereslou.com/2009/05/27/starting-with-spark-in-ten-minutes#comments</comments>
		<pubDate>Wed, 27 May 2009 14:49:50 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[spark]]></category>

		<category><![CDATA[dimecast]]></category>

		<category><![CDATA[internet]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=500</guid>
		<description><![CDATA[Earlier today Donn Felker blogged about his new DimeCast episode: 
# 113 - Saying Hello to the Spark View Engine
In this episode we are going to start a series where we take an indepth look at the Spark View Engine.
Spark is a view engine for Asp.Net Mvc and Castle Project MonoRail frameworks. The idea is [...]]]></description>
			<content:encoded><![CDATA[<p>Earlier today <a href="http://blog.donnfelker.com/post/Saying-Hello-to-the-Spark-View-Engine.aspx">Donn Felker blogged</a> about his new <a href="http://www.dimecasts.net">DimeCast</a> episode: </p>
<blockquote><p><strong><a href="http://www.dimecasts.net/Casts/CastDetails/113"># 113 - Saying Hello to the Spark View Engine</a></strong></p>
<p>In this episode we are going to start a series where we take an indepth look at the Spark View Engine.</p>
<p>Spark is a view engine for Asp.Net Mvc and Castle Project MonoRail frameworks. The idea is to allow the html to dominate the flow and the code to fit seamlessly.</p></blockquote>
<p>Very cool. Can&#8217;t wait to see it on a pc with audio.</p>
<p>It also reminds me I need to revamp the front page of the <a href="http://sparkviewengine.com">project site</a>. I&#8217;d like for it to start with something like a quick article about creating a spark app instead of a bunch of random links.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/fP5TOvcEPoU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/05/27/starting-with-spark-in-ten-minutes/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spark Macro for Uploadify, Take One-B</title>
		<link>http://whereslou.com/2009/05/22/spark-macro-for-uploadify-take-one-b</link>
		<comments>http://whereslou.com/2009/05/22/spark-macro-for-uploadify-take-one-b#comments</comments>
		<pubDate>Sat, 23 May 2009 04:37:46 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[spark]]></category>

		<category><![CDATA[aspnetmvc]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=487</guid>
		<description><![CDATA[Matt recently wrote in ASP.NET MVC HtmlHelper for Uploadify, Take One about a fairly common problem in the shades of gray where html and code concerns overlap. It&#8217;s a game of trying to get the least of both evils mixed in with the other.
Matt makes a particularly insightful remark in a comment, &#8220;what I dislike [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://trycatchfail.com/blog/author/Matt.aspx">Matt</a> recently wrote in <a href="http://trycatchfail.com/blog/post/2009/05/13/ASPNET-MVC-HtmlHelper-for-Uploadify-Take-One.aspx">ASP.NET MVC HtmlHelper for Uploadify, Take One</a> about a fairly common problem in the shades of gray where html and code concerns overlap. It&#8217;s a game of trying to get the least of both evils mixed in with the other.</p>
<p>Matt makes a particularly insightful remark in a comment, &#8220;what I dislike is how the balance of language in the helpers tends to shift from C# to JavaScript or HTML.&#8221;</p>
<p>I&#8217;d agree with that. On the one hand you&#8217;re trying to avoid excessive code in your template, but in doing so you&#8217;re dragging markup into your code. It&#8217;s actually a great example of a primary use-case for a macro in the Spark language, which lets you declare a helper method in the template language.</p>
<p>Here is the HtmlHelper extension exactly as it appeared on Matt&#8217;s post:</p>
<pre class="brush: csharp;">
/// &lt;summary&gt;
/// Renders JavaScript to turn the specified file input control into an
/// Uploadify upload control.
/// &lt;/summary&gt;
/// &lt;param name=&quot;helper&quot;&gt;&lt;/param&gt;
/// &lt;param name=&quot;name&quot;&gt;&lt;/param&gt;
/// &lt;param name=&quot;options&quot;&gt;&lt;/param&gt;
/// &lt;returns&gt;&lt;/returns&gt;
public static string Uploadify(this HtmlHelper helper, string name, UploadifyOptions options)
{
    string scriptPath = helper.ResolveUrl(&quot;~/Content/jqueryPlugins/uploadify/&quot;);

    StringBuilder sb = new StringBuilder();
    //Include the JS file.
    sb.Append(helper.ScriptInclude(&quot;~/Content/jqueryPlugins/uploadify/jquery.uploadify.js&quot;));
    sb.Append(helper.ScriptInclude(&quot;~/Content/jqueryPlugins/uploadify/jquery.uploadify.init.js&quot;));

    //Dump the script to initialze Uploadify
    sb.AppendLine(&quot;&lt;script type=\&quot;text/javascript\&quot;&gt;&quot;);
    sb.AppendLine(&quot;$(document).ready(function() {&quot;);
    sb.AppendFormat(&quot;initUploadify($('#{0}'),'{1}','{2}','{3}','{4}','{5}',{6},{7});&quot;,
                    name, options.UploadUrl,
                    scriptPath, options.FileExtensions,
                    options.FileDescription, options.AuthenticationToken,
                    options.ErrorFunction ?? &quot;null&quot;,
                    options.CompleteFunction ?? &quot;null&quot;);
    sb.AppendLine();
    sb.AppendLine(&quot;});&quot;);
    sb.AppendLine(&quot;&lt;/script&quot;);

    return sb.ToString();
}
</pre>
<p>And here&#8217;s the same helper implemented as a macro:</p>
<pre class="brush: csharp;">
&lt;macro name=&quot;Uploadify&quot; name=&quot;string&quot; options=&quot;UploadifyOptions&quot;&gt;

  &lt;var scriptPath=&quot;Html.ResolveUrl('~/Content/jqueryPlugins/uploadify/')&quot;/&gt;

  !{Html.ScriptInclude(&quot;~/Content/jqueryPlugins/uploadify/jquery.uploadify.js&quot;)}
  !{Html.ScriptInclude(&quot;~/Content/jqueryPlugins/uploadify/jquery.uploadify.init.js&quot;)}

  &lt;script type=&quot;text/javascript&quot;&gt;
    $(document).ready(function() {
      initUploadify($('#!{name}'), '!{options.UploadUrl}',
        '!{scriptPath}', '!{options.FileExtensions}',
        '!{options.FileDescription}', '!{options.AuthenticationToken}',
        !{options.ErrorFunction ?? &quot;null&quot;},
        !{options.CompleteFunction ?? &quot;null&quot;});
    });
  &lt;/script&gt;

&lt;/macro&gt;
</pre>
<p>The usage of the helper remains nearly identical. It&#8217;s a method on the view now instead of an Html extension method&#8230; So:</p>
<p><code>&lt;%= Html.Uploadify("fileInput", new UploadifyOptions { ... }) %&gt;</code></p>
<p>becomes:</p>
<p><code>&lt;%= Uploadify("fileInput", new UploadifyOptions { ... }) %&gt;</code></p>
<p>or rather:</p>
<p><code>!{Uploadify("fileInput", new UploadifyOptions { ... })}</code></p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/cBcF-fFvjFQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/05/22/spark-macro-for-uploadify-take-one-b/feed</wfw:commentRss>
		</item>
		<item>
		<title>Server upgraded - brief service interruption</title>
		<link>http://whereslou.com/2009/05/20/server-upgraded-brief-service-interruption</link>
		<comments>http://whereslou.com/2009/05/20/server-upgraded-brief-service-interruption#comments</comments>
		<pubDate>Thu, 21 May 2009 03:04:31 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[fastcgi]]></category>

		<category><![CDATA[nginx]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=485</guid>
		<description><![CDATA[The host this runs on was upgraded just now. Runtime for the VM Ubuntu host was 399 days. Hats off to slidehost for that quality of service!
Upgrade went well - some config files showed up as modified so there were some keep/replace prompts. But in general pretty smooth.
Did lose the php-fastcgi daemon in the upgrade [...]]]></description>
			<content:encoded><![CDATA[<p>The host this runs on was upgraded just now. Runtime for the VM Ubuntu host was 399 days. Hats off to slidehost for that quality of service!</p>
<p>Upgrade went well - some config files showed up as modified so there were some keep/replace prompts. But in general pretty smooth.</p>
<p>Did lose the php-fastcgi daemon in the upgrade though. So that was a bit of a rough moment after the reboot when both <a href="http://whereslou.com">http://whereslou.com</a> and <a href="http://sparkviewengine.com">http://sparkviewengine.com</a> were toast.</p>
<p>Fortunately a quick Google search turned up this <a href="http://blog.elderec.com/2008/07/16/ubuntu-hardy-nginx-php5-fastcgi/">blog post from Elderec</a> which got me up and running again.</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/ZQ0fwJwlNUA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/05/20/server-upgraded-brief-service-interruption/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spark in the field - MarketWatch 5.0</title>
		<link>http://whereslou.com/2009/05/16/spark-in-the-field-marketwatch-50</link>
		<comments>http://whereslou.com/2009/05/16/spark-in-the-field-marketwatch-50#comments</comments>
		<pubDate>Sat, 16 May 2009 19:11:52 +0000</pubDate>
		<dc:creator>Louis</dc:creator>
		
		<category><![CDATA[internet]]></category>

		<category><![CDATA[spark]]></category>

		<category><![CDATA[tech]]></category>

		<category><![CDATA[marketwatch]]></category>

		<guid isPermaLink="false">http://whereslou.com/?p=455</guid>
		<description><![CDATA[ The MarketWatch site has recently recently been the target of a massive renovation project. A highlight of some new features from an editorial perspective can be found at the Site tour, but I wanted to take some time to talk about it from a technology point of view.
The initial work for engineering began some [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.marketwatch.com"><img src="http://whereslou.com/wp-content/uploads/2009/05/marketwatch.png" alt="" title="marketwatch" width="295" height="89" class="alignright size-full wp-image-456" /></a> The <a href="http://www.marketwatch.com">MarketWatch</a> site has recently recently been the target of a massive renovation project. A highlight of some new features from an editorial perspective can be found at the <a href="http://www.marketwatch.com/tour">Site tour</a>, but I wanted to take some time to talk about it from a technology point of view.</p>
<p>The initial work for engineering began some time last year, approximately December, we began to create systems known to be necessary for the functionality that would be eventually required. The middle of the first quarter saw the completion of the joint efforts of the design agency and business stakeholders in the form of new IA/UX and design. With an ultimate target date of May 12th I&#8217;m sure you can appreciate the fact a huge number of things about the implementation needed to go very well.</p>
<p>Many people on several teams were involved in it&#8217;s creation. I can&#8217;t possibly name everyone involved but I wanted to recognize a few people who acted in a technical leadership capacity.<br />
<span id="more-455"></span><br />
<a href="http://www.kevindotzenrod.com/">Kevin Dotzenrod</a>, Architect and Engineer Manager, is a good friend I&#8217;ve worked with many times before. He was instrumental in the success of the project in every way from the original solution design to final profiling and field integration. </p>
<p><a href="http://blog.donnfelker.com/">Donn Felker</a>, Senior Consultant with Magenic, is a developer I was working with for the first time but respect greatly. His fluency in the art modern software development, determination, and ability to knock down complex challenges is incomparable. </p>
<p>And the team leads, Kevin Boedigheimer, Peter Gaard, Bob Galuska, Ben Music, and Phil Whipps, have done a spectacular job of engineering individually as well as ensuring the people on their teams could be successful.</p>
<p>But enough cheerleading. :)</p>
<p><strong>Platform</strong></p>
<p><a href="http://www.iis.net/"><img src="http://whereslou.com/wp-content/uploads/2009/05/server-2008-iis-7.png" alt="" title="server-2008-iis-7" width="318" height="109" class="alignright size-full wp-image-468" /></a>We made the decision in the very beginning to shift onto IIS7 on the Windows Server 2008 64-bit platform. Some notable technologies used in the site&#8217;s creation include <a href="http://asp.net/mvc">ASP.NET MVC</a>, <a href="http://sparkviewengine.com">Spark</a>, <a href="http://msdn.microsoft.com/en-us/netframework/aa663324.aspx">WCF</a>, <a href="http://www.castleproject.org/container/index.html">Windsor container</a>, and Active Record over <a href="http://nhforge.org/">NHibernate</a>. </p>
<p><a href="http://www.asp.net/mvc/"><img src="http://whereslou.com/wp-content/uploads/2009/05/mvc-logo-landing-page.png" alt="" title="mvc-logo-landing-page" width="142" height="58" class="alignleft size-full wp-image-466" /></a>That may seem like a risky combination, but the ASP.NET MVC web framework was the only technology we hadn&#8217;t used in production previously. Everything else had a proven track record of stability and ease of use. Because we were transitioning from MonoRail+Spark to MVC+Spark even that change didn&#8217;t involve any differences in concepts and patterns. The learning curve was limited to the differences between in controllers, routing, and html helper implementations. The extensibility of System.Web.Mvc and System.Web.Routing was particularly impressive. With the ability to replace a service at exactly the correct place you can take command of your controller creation to introduce IoC, or your take control of the action/result execution process to inject site-wide filters or policy.</p>
<p>Although the technology was understood a few rough patches for productivity came during the earlier stages of development, which is why you always want to keep an eye out for opportunities to adapt and simplify. A large number of people were working in parallel, and once certain critical superstructure was in place the velocity of development was astonishing. One practice we adopted which worked quite well was to establish a canary deployment as part the last step of a successful build. Not to be confused with qa deployment, or staging for production, or a snapshot for internal demos - the job of the canary (in the coal mine) is to die first when there&#8217;s a new problem. It&#8217;s used as a shared resource to verify correctness of recent changes and the current state of the overall project.</p>
<p><strong>Bluegrass</strong></p>
<p><a href="http://whereslou.com/wp-content/uploads/2009/05/bluegrass.jpg"><img src="http://whereslou.com/wp-content/uploads/2009/05/bluegrass-205x300.jpg" alt="" title="bluegrass" width="205" height="300" class="alignright size-medium wp-image-470" /></a>There&#8217;s no time to dive into all of the different aspects of the implementation but one I wanted to highlight specifically is <em>Bluegrass</em>. For a long time MarketWatch has had a LiveQuotes feature, which evolved into LiveSuite as news was introduced, both of which are now deprecated with with the deployment of Bluegrass - our asynchronous, comet-like distribution framework to deliver instantaneous json data and event information. The client forms at-most-one connection with the a message broker which either returns queued messages or enters an efficient aync wait state on the server until the arrival of messages for that particular caller.</p>
<p>The brokers themselves are ignorant of the meaning of the messages, a change from the design of LiveQuotes and LiveSuite, which will enable us to provide additional channels of information without updating and redeploying those systems. The scale at which they operate is also unprecedented in our organization. The servers will each process 3700 requests a second at sixty percent cpu (dual quad-cores) and will top out at six thousand. And that&#8217;s while each server averages over ten thousand concurrent requests executing as several calls to pickup are &#8220;on hold&#8221; pending the arrival of any messages for the caller.</p>
<p><strong>Windsor</strong></p>
<p><a href="http://www.castleproject.org/container"><img src="http://whereslou.com/wp-content/uploads/2009/05/windsor_rawlogo.gif" alt="" title="windsor_rawlogo" width="191" height="123" class="alignright size-full wp-image-479" /></a>For the implementation of the web site itself there were many reasons to keep it simple and direct. Windsor is an Inversion of Control (IoC) container which are very important to understand if you haven&#8217;t used such a thing before. It&#8217;s impossible to describe why you would want to use an IoC container other than to describe it&#8217;s effect with a metaphor. Imagine in your mind the classic surgery scene - the surgeon says &#8220;scalpel&#8221; and <em>whap</em> she has one in her hand, properly sterilized and inventoried pre-op according to policy, so all she does is indicates the need and uses the tool. &#8220;Clamp&#8221; and <em>whap</em> she&#8217;s clamping. Doesn&#8217;t even glance away from the surgery site.</p>
<p>The effect this has on your code is dramatic. As you&#8217;re writing a controller it has a task at hand to worry about. It&#8217;s concern is receiving actions and their parameters and policy, composing view data, returning the appropriate type of result in response. The last thing you need is a lot of code, and the associated knowledge, for create and initializing the services it needs. Just as a surgeon doesn&#8217;t sterilize and inventory the O.R. equiptment she&#8217;ll be using, an IoC container enables your controller&#8217;s constructor to say &#8220;IQuotes,&#8221; &#8220;IHeadlines,&#8221; &#8220;IComments,&#8221; and have the appropriately initialized and lifecycled implemenations of those service components slapped into it&#8217;s hands ready to use.</p>
<p>It&#8217;s a beautiful thing and really helps you abstract away the complexity of interconnectedness with very little effort on what is a fairly large number of components coming together to service a request. We had declared a base IService interface to automatically enlisted the descendant components into the IoC container and that convention eliminated all configuration in the majority of cases. </p>
<p>There&#8217;s more I could go on about - but this is blog, not a short story, so at this point I believe I&#8217;ll thank you for reading this far and leave it at that. :)</p>
<img src="http://feeds.feedburner.com/~r/WheresLou/~4/WaQRVMh51pU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://whereslou.com/2009/05/16/spark-in-the-field-marketwatch-50/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
