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

<channel>
	<title>David Develops (Software) &#187; technical</title>
	<atom:link href="http://daviddevelops.co.uk/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>http://daviddevelops.co.uk</link>
	<description>A technical blog by David Whitney. Software Development, .Net and how to make your projects work.</description>
	<pubDate>Thu, 03 Jun 2010 21:31:18 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Serving different views for mobile devices in ASP.NET MVC</title>
		<link>http://daviddevelops.co.uk/index.php/2010/05/03/serving-different-views-for-mobile-devices-in-asp-net-mvc/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/05/03/serving-different-views-for-mobile-devices-in-asp-net-mvc/#comments</comments>
		<pubDate>Mon, 03 May 2010 21:40:52 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[free software]]></category>

		<category><![CDATA[geek]]></category>

		<category><![CDATA[internet]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2010/05/03/serving-different-views-for-mobile-devices-in-asp-net-mvc/</guid>
		<description><![CDATA[As browsing becomes more commonplace on phones, sub-notebooks and (within the next year or so) Tablet PCs, there’s an increased appetite to tailor your user experience for people using these “non-desktop” devices.&#160; You can leverage your existing application infrastructure without having to create costly or outsource applications for specific (*cough* iPhone) platforms that have lots [...]]]></description>
			<content:encoded><![CDATA[<p>As browsing becomes more commonplace on phones, sub-notebooks and (within the next year or so) Tablet PCs, there’s an increased appetite to tailor your user experience for people using these “non-desktop” devices.&#160; You can leverage your existing application infrastructure without having to create costly or outsource applications for specific (*cough* iPhone) platforms that have lots of market share, to the exclusion of others (*cough* iPhone).</p>
<p>There are some excellent examples of this in the wild, Facebook and the BBC do a great job of this already.</p>
<p>So what about us ASP.NET MVC types?&#160; Well thankfully, there’s a lot of stuff built in to the framework to allow us to do this with relative ease.&#160; First you’re going to need a few things..</p>
<p>1) Go grab the latest Mobile Device Browser File from <a title="http://mdbf.codeplex.com" href="http://mdbf.codeplex.com">http://mdbf.codeplex.com</a>.&#160; If you’re familiar with the old “Browser Caps”, it’s the same sort of thing.&#160; A regularly updated collection of device data.&#160; It’s pretty interesting on it’s own, breaking down incoming devices by user agent / headers and offering you various stats (touch enabled, screen resolution etc).&#160; You’ll probably want to keep this up to date periodically.</p>
<p>2) A new ViewEngine!&#160; Thankfully, ASP.NET MVC has a pretty flexible pipeline when it comes to slotting in new ViewEngines.&#160; The most transparent way to switch out which view you’re serving for any given request is to override some of the logic in the default ViewEngine to look elsewhere when a View is requested by the MVC framework.</p>
<p>3) A way to test this stuff.&#160; I normally break out the “User Agent Switcher” FireFox plugin which you can grab here: <a title="https://addons.mozilla.org/en-US/firefox/addon/59" href="https://addons.mozilla.org/en-US/firefox/addon/59">https://addons.mozilla.org/en-US/firefox/addon/59</a></p>
<p>&#160;</p>
<h2>The View Engine</h2>
<p>In order to keep this as close to vanilla MVC as possible, I’m going to extend the regular WebFormsViewEngine to add mobile device detection and view overriding.&#160; What I’m going to do is check for a mobile browser when a request reaches the view engine, and if one is found, add some extra paths to look for the view files in at the top of the list of locations searched.&#160; By doing this, we can prioritise the mobile edition of a website if the user is visiting from a phone, while degrading gracefully, allowing the regular version of the website to be served if a mobile version of a given page isn’t available.&#160; This actually allows us to support mobile views piece by piece rather than forcing us to support the entire site out of the box.</p>
<p>Unfortunately, a few of the methods surrounding view resolution are marked as private in the WebFormsViewEngine and as such are inaccessible.&#160; I’ve had to reflect in and copy a couple of methods to get around this.&#160; Ideally the access modifier on these could be changed in later versions of the framework.</p>
<p>The key method we have to work with is</p>
<p>public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)   <br />{ … }</p>
<p>What we’re going to do is add a few extra locations based on the browser type.&#160; With the mobile device browser file installed, this is really simple:</p>
<blockquote><p>public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)     <br />{      <br />&#160;&#160;&#160; if (controllerContext == null)      <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentNullException(&quot;controllerContext&quot;);      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; if (String.IsNullOrEmpty(viewName))      <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentException(&quot;viewName&quot;);      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; List&lt;string&gt; viewLocationsSearched;     <br />&#160;&#160;&#160; List&lt;string&gt; masterLocationsSearched; </p>
<p>&#160;&#160;&#160; string[] viewLocationsToSearch = ViewLocationFormats;     <br />&#160;&#160;&#160; string[] masterLocationsToSearch = MasterLocationFormats; </p>
<p>&#160;&#160;&#160; viewLocationsToSearch = AddMobileViewLocations(controllerContext, viewLocationsToSearch, MobileViewLocationFormats);     <br />&#160;&#160;&#160; masterLocationsToSearch = AddMobileViewLocations(controllerContext, masterLocationsToSearch, MobileMasterLocationFormats); </p>
<p>&#160;&#160;&#160; string controllerName = controllerContext.RouteData.GetRequiredString(&quot;controller&quot;);     <br />&#160;&#160;&#160; string viewPath = GetPath(controllerContext, viewLocationsToSearch, viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched);      <br />&#160;&#160;&#160; string masterPath = GetPath(controllerContext, masterLocationsToSearch, masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched); </p>
<p>&#160;&#160;&#160; if (String.IsNullOrEmpty(viewPath)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; || (String.IsNullOrEmpty(masterPath)       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &amp;&amp; !String.IsNullOrEmpty(masterName)))      <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);     <br />} </p>
</blockquote>
<p>You’ll notice there’s a few methods that get called in there.&#160; The most important of which is “AddMobileViewLocations”.&#160; This really is where all the legwork is done, and looks like this</p>
<blockquote><p>public class SwitchingViewEngine : WebFormViewEngine     <br />{      <br />&#160;&#160;&#160; private const string CacheKeyFormat = &quot;:ViewCacheEntry:{0}:{1}:{2}:{3}:&quot;;      <br />&#160;&#160;&#160; private const string CacheKeyPrefixMaster = &quot;Master&quot;;      <br />&#160;&#160;&#160; private const string CacheKeyPrefixView = &quot;View&quot;;      <br />&#160;&#160;&#160; private static readonly List&lt;string&gt; EmptyLocations = new List&lt;string&gt;(); </p>
<p>&#160;&#160;&#160; protected string[] MobileViewLocationFormats { get; private set; }     <br />&#160;&#160;&#160; protected string[] MobileMasterLocationFormats { get; private set; } </p>
<p>&#160;&#160;&#160; public SwitchingViewEngine()     <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationFormats = new[]      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/{1}/{0}.aspx&quot;, &quot;~/Views/{1}/{0}.ascx&quot;, &quot;~/Views/Shared/{0}.aspx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.ascx&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; MobileViewLocationFormats = new[]     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/{1}/{0}.mobile.aspx&quot;, &quot;~/Views/{1}/{0}.mobile.ascx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.mobile.aspx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.mobile.ascx&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; MasterLocationFormats = new[] {&quot;~/Views/{1}/{0}.master&quot;, &quot;~/Views/Shared/{0}.master&quot;};     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; MobileMasterLocationFormats = new[] {&quot;~/Views/{1}/{0}.mobile.master&quot;, &quot;~/Views/Shared/{0}.mobile.master&quot;};      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)     <br />&#160;&#160;&#160; {&#160;&#160; …&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; private static string[] AddMobileViewLocations(ControllerContext controllerContext,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string[] viewLocationsToSearch,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IEnumerable&lt;string&gt; mobileViewLocations)      <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (controllerContext == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext.Request == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext.Request.Browser == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || viewLocationsToSearch == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || viewLocationsToSearch.Length == 0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || mobileViewLocations == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || mobileViewLocations.ToList().Count == 0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || !controllerContext.HttpContext.Request.Browser.IsMobileDevice)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return viewLocationsToSearch;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; var mobileViews = viewLocationsToSearch.ToList();     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (var view in mobileViewLocations.Reverse())      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mobileViews.Insert(0, view);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; viewLocationsToSearch = mobileViews.ToArray(); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return viewLocationsToSearch;     <br />&#160;&#160;&#160; } </p>
</blockquote>
<p>This method takes the current ViewLocations that are defined at the top of the class, does a bunch of guard checks (to prevent mobile switching crashing your request.. call me paranoid), then verifies that controllerContext.HttpContext.Request.Browser.IsMobileDevice is true.&#160; This check makes use of the browser device file.&#160; If all the checks pass, it inserts the new “mobile view paths” at the very top of the list of paths to be searched when resolving the location of a view file.&#160; The calling method then subsequently calls the method “GetPath” (which is one of the private methods I’ve had to reflect out of the framework source code).&#160; GetPath searches the supplied list of potential view locations, and returns as soon as it finds a match.&#160; In our case, if the browser is mobile, and a view file with the extension mobile.aspx is found, this will be the first view resolved and returned.</p>
<p>The full code listing for the view engine (don’t worry, there’s a link at the bottom to grab all of this in one archive):</p>
<blockquote><p>using System;     <br />using System.Collections.Generic;      <br />using System.Globalization;      <br />using System.Linq;      <br />using System.Web.Mvc; </p>
<p>namespace MultipleViewMvcExample.DemoCode     <br />{      <br />&#160;&#160;&#160; public class SwitchingViewEngine : WebFormViewEngine      <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyFormat = &quot;:ViewCacheEntry:{0}:{1}:{2}:{3}:&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyPrefixMaster = &quot;Master&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyPrefixView = &quot;View&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private static readonly List&lt;string&gt; EmptyLocations = new List&lt;string&gt;(); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; protected string[] MobileViewLocationFormats { get; private set; }     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; protected string[] MobileMasterLocationFormats { get; private set; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public SwitchingViewEngine()     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationFormats = new[]      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/{1}/{0}.aspx&quot;, &quot;~/Views/{1}/{0}.ascx&quot;, &quot;~/Views/Shared/{0}.aspx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.ascx&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MobileViewLocationFormats = new[]     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/{1}/{0}.mobile.aspx&quot;, &quot;~/Views/{1}/{0}.mobile.ascx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.mobile.aspx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.mobile.ascx&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MasterLocationFormats = new[] {&quot;~/Views/{1}/{0}.master&quot;, &quot;~/Views/Shared/{0}.master&quot;};     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MobileMasterLocationFormats = new[] {&quot;~/Views/{1}/{0}.mobile.master&quot;, &quot;~/Views/Shared/{0}.mobile.master&quot;};      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (controllerContext == null)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentNullException(&quot;controllerContext&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (String.IsNullOrEmpty(viewName))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentException(&quot;viewName&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; List&lt;string&gt; viewLocationsSearched;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; List&lt;string&gt; masterLocationsSearched; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string[] viewLocationsToSearch = ViewLocationFormats;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string[] masterLocationsToSearch = MasterLocationFormats; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; viewLocationsToSearch = AddMobileViewLocations(controllerContext, viewLocationsToSearch, MobileViewLocationFormats);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; masterLocationsToSearch = AddMobileViewLocations(controllerContext, masterLocationsToSearch, MobileMasterLocationFormats); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string controllerName = controllerContext.RouteData.GetRequiredString(&quot;controller&quot;);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string viewPath = GetPath(controllerContext, viewLocationsToSearch, viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string masterPath = GetPath(controllerContext, masterLocationsToSearch, masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (String.IsNullOrEmpty(viewPath)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || (String.IsNullOrEmpty(masterPath)       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &amp;&amp; !String.IsNullOrEmpty(masterName)))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private static string[] AddMobileViewLocations(ControllerContext controllerContext,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string[] viewLocationsToSearch,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IEnumerable&lt;string&gt; mobileViewLocations)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (controllerContext == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext.Request == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || controllerContext.HttpContext.Request.Browser == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || viewLocationsToSearch == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || viewLocationsToSearch.Length == 0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || mobileViewLocations == null      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || mobileViewLocations.ToList().Count == 0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || !controllerContext.HttpContext.Request.Browser.IsMobileDevice)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return viewLocationsToSearch;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var mobileViews = viewLocationsToSearch.ToList();     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (var view in mobileViewLocations.Reverse())      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mobileViews.Insert(0, view);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; viewLocationsToSearch = mobileViews.ToArray(); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return viewLocationsToSearch;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPath(ControllerContext controllerContext, string[] locations, string name,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string controllerName, string cacheKeyPrefix, bool useCache,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; out List&lt;string&gt; searchedLocations)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = EmptyLocations;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (string.IsNullOrEmpty(name))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return string.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((locations == null) || (locations.Length == 0))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new InvalidOperationException(&quot;Property cannot be null or empty.&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; bool flag = IsSpecificPath(name);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string key = CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (useCache)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string viewLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (viewLocation != null)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return viewLocation;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!flag)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromGeneralName(controllerContext, locations, name, controllerName, key,ref searchedLocations);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private static bool IsSpecificPath(string name)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; char ch = name[0];      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (ch != &#8216;~&#8217;)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return (ch == &#8216;/&#8217;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return true;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ref List&lt;string&gt; searchedLocations)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string virtualPath = name;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!FileExists(controllerContext, name))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; virtualPath = string.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = new List&lt;string&gt; {name};      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return virtualPath;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string controllerName, string cacheKey, ref List&lt;string&gt; searchedLocations)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string virtualPath = string.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = new List&lt;string&gt;();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for (int i = 0; i &lt; locations.Length; i++)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string str2 = string.Format(CultureInfo.InvariantCulture, locations[i],      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; new object[] {name, controllerName});      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (FileExists(controllerContext, str2))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = EmptyLocations;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; virtualPath = str2;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return virtualPath;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations[i] = str2;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return virtualPath;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string CreateCacheKey(string prefix, string name, string controllerName)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return String.Format(CultureInfo.InvariantCulture, CacheKeyFormat,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; GetType().AssemblyQualifiedName, prefix, name, controllerName);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }      <br />}</p>
<p>&#160;</p>
</blockquote>
<h2>The Wiring</h2>
<p>Now you have your view engine, you need to register it as the default view engine in your MVC application.&#160; Easy!&#160; Open up your Global.aspx.cs file and add the following to ApplicationStart</p>
<blockquote><p>ViewEngines.Engines.Clear();     <br />ViewEngines.Engines.Add(new SwitchingViewEngine());</p>
</blockquote>
<p>Done!</p>
<p>Now you need to actually add the browser detection file to your application.&#160; Presuming you downloaded the latest archive from the codeplex url at the top of this article, all you need to do is copy the supplied mobile.browser file to App_Browsers/Device/* in your MVC application.</p>
<p>That’s all the wiring you need to get everything up and running.</p>
<p>If you’ve done it right, a default “New MVC Template” project with these additions might look something like this:</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image.png" rel="lightbox"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image_thumb.png" width="266" height="572" /></a> </p>
<p>For the sake of this demo, I put the view engine in a “DemoCode” sub-namespace. You don’t want to do that, put it somewhere sensible!</p>
<p>The more astute reader might now notice that my Views/Home directory in the above screenshot has an extra file, not supplied by the template, called “Index.mobile.aspx”.&#160; Likewise, my /Views/Shared directory has Site.mobile.Master.&#160; These are files I want the view engine to resolve if a user hits the <a href="http://localhost/Home">http://localhost/Home</a> default route from a mobile device.</p>
<p>Index.mobile.aspx looks like this:</p>
<blockquote><p>&lt;%@ Page Language=&quot;C#&quot; MasterPageFile=&quot;~/Views/Shared/Site.mobile.Master&quot; Inherits=&quot;System.Web.Mvc.ViewPage&quot; %&gt; </p>
<p>&lt;asp:Content ID=&quot;indexTitle&quot; ContentPlaceHolderID=&quot;TitleContent&quot; runat=&quot;server&quot;&gt;     <br />&#160;&#160;&#160; Mobile Home Page      <br />&lt;/asp:Content&gt; </p>
<p>&lt;asp:Content ID=&quot;indexContent&quot; ContentPlaceHolderID=&quot;MainContent&quot; runat=&quot;server&quot;&gt;     <br />&#160;&#160;&#160; &lt;h2&gt;&lt;%= Html.Encode(ViewData[&quot;Message&quot;]) %&gt;&lt;/h2&gt;      <br />&#160;&#160;&#160; This is my mobile index page.      <br />&lt;/asp:Content&gt;</p>
</blockquote>
<p>and Site.mobile.Master looks like this:</p>
<blockquote><p>&lt;%@ Master Language=&quot;C#&quot; Inherits=&quot;System.Web.Mvc.ViewMasterPage&quot; %&gt; </p>
<p>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;<a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;">http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;</a>&gt;      <br />&lt;html xmlns=&quot;<a href="http://www.w3.org/1999/xhtml&quot;">http://www.w3.org/1999/xhtml&quot;</a>&gt;      <br />&lt;head runat=&quot;server&quot;&gt;      <br />&#160;&#160;&#160; &lt;title&gt;&lt;asp:ContentPlaceHolder ID=&quot;TitleContent&quot; runat=&quot;server&quot; /&gt;&lt;/title&gt;      <br />&lt;/head&gt; </p>
<p>&lt;body&gt;     <br />&#160;&#160;&#160; &lt;div class=&quot;page&quot;&gt; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;header&quot;&gt;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;title&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;h1&gt;My MVC Application - mobile master page&lt;/h1&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;logindisplay&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;% Html.RenderPartial(&quot;LogOnUserControl&quot;); %&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;menucontainer&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ul id=&quot;menu&quot;&gt;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;%= Html.ActionLink(&quot;Home&quot;, &quot;Index&quot;, &quot;Home&quot;)%&gt;&lt;/li&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;%= Html.ActionLink(&quot;About&quot;, &quot;About&quot;, &quot;Home&quot;)%&gt;&lt;/li&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/ul&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;main&quot;&gt;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;asp:ContentPlaceHolder ID=&quot;MainContent&quot; runat=&quot;server&quot; /&gt; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div id=&quot;footer&quot;&gt;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/div&gt;      <br />&#160;&#160;&#160; &lt;/div&gt;      <br />&lt;/body&gt;      <br />&lt;/html&gt;</p>
</blockquote>
<p>Nothing especially revolutionary.</p>
<p>&#160;</p>
<h2>Testing</h2>
<p>First, install that FireFox plugin I mentioned at the top of the article, create a new MVC project (just use the template), and wire up the view engine and browser file.&#160; Add some extra views with the .mobile.aspx prefix.&#160; Or download the sample attached to the bottom of this post!</p>
<p>Start the site up in Cassini (Visual Studios default web server) and hit /Home.&#160; You should see this:</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image1.png" rel="lightbox"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image_thumb1.png" width="692" height="566" /></a> </p>
<p>Now, lets use the new FireFox plugin…</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image2.png" rel="lightbox"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image_thumb2.png" width="506" height="245" /></a> </p>
<p>Select iPhone 3.0 from that menu and refresh the page…</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image3.png" rel="lightbox"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2010/05/image_thumb3.png" width="593" height="485" /></a> </p>
<p>Bang!&#160; The more eagle eye reader might have notice that all I did in my “mobile” master page, was delete the CSS reference from the default MVC template, thus the above screenshot.</p>
<p>And that’s it.</p>
<p>&#160;</p>
<h2>Further Thoughts</h2>
<p>This solution goes quite a way, but here are a few other ideas:</p>
<ul>
<li>Don’t just do browser type detection, detect and switch on subdomain, so any visitors hitting <a href="http://m.mysite.com">http://m.mysite.com</a> get a different view.</li>
<li>Allow the user to opt-out of the reduced view with a session cookie.</li>
<li>Switch views to a low-fi version to victimise IE6 users!</li>
<li>Target tablet PCs based on resolution to build a “touch UI”</li>
</ul>
<p>It’s all pretty simple.&#160; The really nice thing about this solution is that your designers can just add these mobile views as and when they see fit, as the same action methods that execute for the “full fat” website are run, and the same strongly typed view models (which you’re using, right? get out of here with your ViewData..) are delivered.&#160; Designers can implement portable websites, piece by piece, just with a little view engine change.</p>
<p>As with all internet code, your mileage may vary, but this technique works for me.</p>
<h3><strong>You can download a working VS2008 solution (so long as you have ASP.NET MVC1 installed on your system) containing all the code used above from: </strong><a href="http://github.com/davidwhitney/MultipleViewMvcExample"><strong>http://github.com/davidwhitney/MultipleViewMvcExample</strong></a></h3>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/05/03/serving-different-views-for-mobile-devices-in-asp-net-mvc/feed/</wfw:commentRss>
		</item>
		<item>
		<title>MobileTFL 1.1.0.0</title>
		<link>http://daviddevelops.co.uk/index.php/2010/04/17/mobiletfl-1-1-0-0/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/04/17/mobiletfl-1-1-0-0/#comments</comments>
		<pubDate>Sat, 17 Apr 2010 12:25:03 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[code]]></category>

		<category><![CDATA[free software]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/?p=228</guid>
		<description><![CDATA[A really quick note, MobileTFL, the London Tube status application for Windows Mobile 6+, has just been updated to version 1.1.0.0.
Changes: Supported new TFL data feed format, fixed breaking bug.
Get it here: http://www.davidwhitney.co.uk/content/blog/index.php/software/
I&#8217;m currently an Android user while awaiting Windows Phone 7, so I&#8217;ve only been able to test this against unit tests and the [...]]]></description>
			<content:encoded><![CDATA[<p>A really quick note, MobileTFL, the London Tube status application for Windows Mobile 6+, has just been updated to version 1.1.0.0.</p>
<p>Changes: Supported new TFL data feed format, fixed breaking bug.</p>
<p>Get it here: http://www.davidwhitney.co.uk/content/blog/index.php/software/</p>
<p>I&#8217;m currently an Android user while awaiting Windows Phone 7, so I&#8217;ve only been able to test this against unit tests and the device emulator, but it looks fine.  That said, feedback would be appreciated.</p>
<p>Thanks to all the people that emailed me to let me know the app had broken.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/04/17/mobiletfl-1-1-0-0/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Automatic Html Encoding in ASP.NET 4.0</title>
		<link>http://daviddevelops.co.uk/index.php/2010/04/13/automatic-html-encoding-in-asp-net-4-0/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/04/13/automatic-html-encoding-in-asp-net-4-0/#comments</comments>
		<pubDate>Tue, 13 Apr 2010 22:40:02 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/?p=224</guid>
		<description><![CDATA[I spent today at Microsofts Techdays Visual Studio 2010 launch event in London.  Lots of interesting stuff, covered in depth all over the internet (just Google, or bing, if you&#8217;re a sadist &#8220;changes from .NET 3.5 to .NET 4.0&#8243;).  A tiny thing that caught my attention for it&#8217;s pure utility to the masses is a [...]]]></description>
			<content:encoded><![CDATA[<p>I spent today at Microsofts Techdays Visual Studio 2010 launch event in London.  Lots of interesting stuff, covered in depth all over the internet (just Google, or bing, if you&#8217;re a sadist &#8220;changes from .NET 3.5 to .NET 4.0&#8243;).  A tiny thing that caught my attention for it&#8217;s pure utility to the masses is a new operator in ASP.NET 4.0 that deals with Html Encoding of data implicity.</p>
<p>You&#8217;ll likely be familiar (especially if you&#8217;re working with ASP.NET MVC) with the &lt;%= notation for referencing properties in the context of the ASP page.  In ASP.NET 4.0 this has been joined by &lt;%: This addition automagically HtmlEncodes any content between the opening and closing tags to prevent repetitive tag soup of &lt;%=HttpUtility.HtmlEncode(myProperty)%&gt; all over your views, and removes the temptation to push HtmlEncoding into your Controller or Model, two places where encoding really shouldn&#8217;t be a concern.</p>
<p>Just struck me as a nice little change that&#8217;ll make everyday life that little bit easier for a majority of web developers. Sure it&#8217;s not quite the new Xaml designer (which is a work of beauty) or Intellitrace (which looked like a total game changer for retrospective debugging), but you know, it&#8217;s the little things that count.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/04/13/automatic-html-encoding-in-asp-net-4-0/feed/</wfw:commentRss>
		</item>
		<item>
		<title>What Are APIs Anyway?</title>
		<link>http://daviddevelops.co.uk/index.php/2010/02/19/what-are-apis-anyway/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/02/19/what-are-apis-anyway/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 11:01:30 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[Software patterns]]></category>

		<category><![CDATA[WCF]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[geek]]></category>

		<category><![CDATA[internet]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/?p=221</guid>
		<description><![CDATA[What are APIs anyway?
Everyone’s heard of APIs these days.  Facebook has them, Twitter has them, Hotmail has them, Microsoft Office has them, Windows has them, Mac OS has them, pretty much everything has them.  I’m going to try and explain in simple terms, but also, almost by contradiction, in detail, what an API really is.  [...]]]></description>
			<content:encoded><![CDATA[<h3>What are APIs anyway?</h3>
<p>Everyone’s heard of APIs these days.  Facebook has them, Twitter has them, Hotmail has them, Microsoft Office has them, Windows has them, Mac OS has them, pretty much everything has them.  I’m going to try and explain in simple terms, but also, almost by contradiction, in detail, what an API really is.  I’ll try and explain why SOAP isn’t how you clean yourself, and how being restful isn’t the same as being lazy.</p>
<p>APIs aren’t new, in fact, APIs are really really old.</p>
<p>Let’s start with a really simple definition.  API is an acronym and it stands for “Application Programming Interface”</p>
<p>To steal the current definition from Wikipedia “An <strong>application programming interface</strong> (<strong>API</strong>) is an <a title="Interface (computer science)" href="http://en.wikipedia.org/wiki/Interface_(computer_science)">interface</a> implemented by a <a title="Computer program" href="http://en.wikipedia.org/wiki/Computer_program">software program</a> to enable interaction with other software, much in the same way that a <a title="User interface" href="http://en.wikipedia.org/wiki/User_interface">user interface</a> facilitates interaction between humans and computers. APIs are implemented by <a title="Application software" href="http://en.wikipedia.org/wiki/Application_software">applications</a>, <a title="Library (computer science)" href="http://en.wikipedia.org/wiki/Library_(computer_science)">libraries</a> and <a title="Operating system" href="http://en.wikipedia.org/wiki/Operating_system">operating systems</a> to determine the vocabulary and <a title="Calling convention" href="http://en.wikipedia.org/wiki/Calling_convention">calling conventions</a> the <a title="Programmer" href="http://en.wikipedia.org/wiki/Programmer">programmer</a> should employ to use their services. It may include specifications for <a title="Subroutine" href="http://en.wikipedia.org/wiki/Subroutine">routines</a>, <a title="Data structure" href="http://en.wikipedia.org/wiki/Data_structure">data structures</a>, <a title="Class (computer science)" href="http://en.wikipedia.org/wiki/Class_(computer_science)">object classes</a> and <a title="Protocol (computing)" href="http://en.wikipedia.org/wiki/Protocol_(computing)">protocols</a> used to communicate between the consumer and implementer of the API.”</p>
<p>That’s a mouthful.  So to translate that into English, an API is a pre-defined set of “stuff” that allows one program to “talk” to another program.  This “stuff” is normally a set of “functions” that another program can call which makes the program being called do something.  Sometimes that other program is really just another part of the same program, and sometimes it’s a different system on a different machine in a different country.  That something is normally described in some kind of documentation, written somewhere, by someone.</p>
<p>If this sounds really vague it’s because in the real world, it really is.  Some of the first “APIs” involved dropped text files into directories on a computer that another program would watch for a read from, and subsequently do “something”.  Arguably the most widely used API is the one that we use to write software for Windows (the Win32 API) and by contrast, that’s a C++ library of code that you can only call if you’re a programmer.  In the real world, we’ve tried to solve some of this ambiguity by standardizing the way APIs work around a few common bits of technology; both for our own sanity and to hopefully help software all just kind of work together.</p>
<p>A lot of APIs are code libraries called by other code libraries to do a specific task.  Microsoft released DirectX to deal with 3d graphics, OpenGL offered an open alternative.  Microsoft released the Windows API for Windows development; Apple released Carbon and Cocoa to program Mac OS.  That said when you hear about or discuss APIs casually, what you’re probably thinking about are actually “Web APIs”.</p>
<h3>Web APIs</h3>
<p>A Web API is an API like anything else, except it’s designed to work over the web.  As a result of this, since about 1994, there have been a number of efforts by a number of people (yes, that vague again!) to standardize the way systems communicate over the internet.</p>
<p>At first, everyone kind of invented their own way of communicating over the internet, people would connect to a server on some port (a port is just a pre-defined “way in”) and send some random pre-defined data in there, and that would make the computer at the other end do something.  Every API was different, and every time that you needed to talk to a different system, you had a really steep learning curve to work out how to talk to every application you wanted to integrate with.  It was a bit rubbish really.</p>
<p>So in 1998 a bunch of really smart people (<a title="Dave Winer" href="http://en.wikipedia.org/wiki/Dave_Winer">Dave Winer</a>, <a title="Don Box" href="http://en.wikipedia.org/wiki/Don_Box">Don Box</a>, Bob Atkinson, and Mohsen Al-Ghosein) got together and came up with SOAP (“Simple Object Access Protocol”).  SOAP is a big bunch of XML that was designed (and ratified) as a standard language that every different system could understand. SOAP is often also referred to as “web services”.</p>
<p>Basically it was designed to reduce the learning curve of learning the details of each system.  So now, if Timmy wanted to talk to Peters shiny new web application, he could point his developer tools at a standard location (something called a WSDL (web service description) document) and he could just ask Peters web application what it did, and how he could use it.</p>
<p>SOAP was actually pretty good and solved a lot of problems and is still widely used today.  It really made waves by helping Microsoft software work pretty well with Java software and everyone was happy.</p>
<p>Almost.</p>
<p>See, because SOAP was solving a lot of big problems in freaking huge enterprise systems, it had a lot to be concerned about; lots of security, lots of encryption, lots of authentication.   That meant that the SOAP “language” was pretty big.  As an example, here (stolen from Wikipedia again!) is the SOAP message that would be sent across the internet to get the current stock price of IBM, from some cool stock price giving web service.</p>
<p>POST /InStock HTTP/1.1</p>
<p>Host: www.example.org</p>
<p>Content-Type: application/soap+xml; charset=utf-8</p>
<p><strong>&lt;?xml</strong> version=&#8221;1.0&#8243;<strong>?&gt;</strong></p>
<p><strong>&lt;soap:Envelope</strong></p>
<p>xmlns:soap=&#8221;http://www.w3.org/2001/12/soap-envelope&#8221;</p>
<p>soap:encodingStyle=&#8221;http://www.w3.org/2001/12/soap-encoding&#8221;<strong>&gt;</strong></p>
<p><strong>&lt;soap:Body</strong> xmlns:m=&#8221;http://www.example.org/stock&#8221;<strong>&gt;</strong></p>
<p><strong>&lt;m:GetStockPrice&gt;</strong></p>
<p><strong>&lt;m:StockName&gt;</strong>IBM<strong>&lt;/m:StockName&gt;</strong></p>
<p><strong>&lt;/m:GetStockPrice&gt;</strong></p>
<p><strong>&lt;/soap:Body&gt;</strong></p>
<p><strong>&lt;/soap:Envelope&gt;</strong></p>
<p>People soon started thinking “Look at all that crap! What’s it all for! Why do I need it?! There’s lots of overhead here!  It slows me down!” and everyone thought “oh actually, well said” and RESTful web services were born.</p>
<p>REST is yet another stupid acronym that actually doesn’t count as an acronym because it uses random letters but developers think is either cool clever or funny.  What it claims to mean is “REpresentational State Transfer” and was largely both a reaction to the complexity and overheads of SOAP, but also being designed by Roy Fielding (one of the authors of the “Hypertext transfer protocol”, the silly http:// bit you type in a browser) it was conceived as an API model that was “closer to the nature of the web”.</p>
<p>What this means to us lay folk, is that while SOAP can technically be used over other protocols (it’s not bound to http), and as such has to bake in security and authentication models into its protocol, REST is designed specifically for the web, it doesn’t work without the web, and it wouldn’t make any sense without the web.  Fielding basically decided that “the web does all of that stuff anyway”, we have security via https / SSL certificates, we have semantics that describe getting and pushing data in the HTTP headers (HTTP headers explain what you’re trying to do to the server you’re connecting to, for example, you use GET for getting stuff, POST and PUT for doing stuff), so let’s just use that and be done with it.</p>
<p>As a result of this way of thinking, REST is a simplified and less general Web API pattern, not concerned with infrastructure like SOAP is.  I’ll convert the above stock price example into a REST example below.</p>
<p>GET /Stocks/Price/IBM HTTP/1.1</p>
<p>Host: www.example.org</p>
<p>Content-Type: application/xml; charset=utf-8</p>
<p>Done.</p>
<p>Basically, REST takes out all the fluff, an decides that if you want to get a stock price for IBM, just make a GET request to the URL <a href="http://www.example.org/Stocks/Price/IBM">http://www.example.org/Stocks/Price/IBM</a> and let the web server at the other end work out what to do from the URL.</p>
<p>The above example would probably return an XML document that looks like this.</p>
<p>&lt;stocks&gt;&lt;stock name=”IBM” price=”3.45”/&gt;&lt;/stocks&gt;</p>
<p>People have started to gravitate towards REST due to its simplicity.  There’s lots of other stuff a RESTful web service should do, it should provided you with all the information that a computer would need to go through a process, just like a user interface gives the user all the information they need to click through a process, but fundamentally it’s simple and leverages the existing semantics of the web to its advantage.</p>
<h3>Ok, Give Me One Of Those!</h3>
<p>So now we know what an API is there to do (let two systems talk to each other) and how they do it (as a general rule, either via SOAP or RESTful services) and we know that everyone else has one, let’s get one of our own.</p>
<p>There are plenty of tools available in pretty much any programming language to make making creating APIs pretty easy.  There are plenty of design concerns to take into account when building an API but in my opinion, the way to approach the problem is to work out what your users really want to do with your application, website or platform, and let them do it.</p>
<p>A good API lets another programmer talk to your system using terms that he understands, so take the time building up a glossary of your business terms vs. what the public understands those concepts to be.  Don’t build API methods that look like:</p>
<p>/PaymentResolutionProcess/PaymentResolveTable3/Resolve/EntityId/123</p>
<p>because it means nothing to anyone, instead, build a bunch of methods that make sense for people to use, the above example could look like this instead:</p>
<p>/Payment/MakePayment/123</p>
<p>Watch your language, and build sensible interactions with your system.  Don’t make APIs for stuff that isn’t going to be used, and where possible, just have the APIs call the same code that your website does.</p>
<p>Get this right, and you’re on a gradual but successful road to calling your “website” a “platform”.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/02/19/what-are-apis-anyway/feed/</wfw:commentRss>
		</item>
		<item>
		<title>C# Access Modifiers Are Type Specific, NOT Instance Specific</title>
		<link>http://daviddevelops.co.uk/index.php/2010/02/17/c-access-modifiers-are-type-specific-not-instance-specific/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/02/17/c-access-modifiers-are-type-specific-not-instance-specific/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 10:01:16 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[C#]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/?p=217</guid>
		<description><![CDATA[Here&#8217;s an interesting example from a brief discussion I was having on twitter yesterday with @DotNetWill.
Did you realize that access modifiers in .NET are type specific rather than instance specific.  It’s not a weird edge case, it is exactly how the language spec lays it out, but it’s not how most people think access [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s an interesting example from a brief discussion I was having on twitter yesterday with @DotNetWill.</p>
<p>Did you realize that access modifiers in .NET are type specific rather than instance specific.  It’s not a weird edge case, it is exactly how the language spec lays it out, but it’s not how most people think access modifiers work.  This is because it’s not very often that any given type will have a reference to ANOTHER instance of that type, it just tends to not come up.</p>
<p>Either way, could make for some hilariously difficult debugging if you weren’t aware of it and something was “playing with your privates” by reference.</p>
<p>You might have seen this kind of usage in a singlet*on, constructor or builder class (or as @jagregory pointed out, cloning methods), but generally it’s just not a very common usage example.  However, it WILL both compile and execute.</p>
<pre>
// Example of access modifiers being specific to a type not an instance
public class MyType
{
     private MyType _innerMyType;

     public MyType()
     {
     }

     public void MakeInnerMyType()
     {
          _innerMyType = new MyType();
          _innerMyType._innerMyType = new MyType();
     }
}
</pre>
<p>Nothing ground breaking, but a fun and interesting little example illustrating a common misconception.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/02/17/c-access-modifiers-are-type-specific-not-instance-specific/feed/</wfw:commentRss>
		</item>
		<item>
		<title>ASP.NET MVC View Engine That Supports View Path Inheritance</title>
		<link>http://daviddevelops.co.uk/index.php/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 00:31:50 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[C#]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/</guid>
		<description><![CDATA[I was working on a small MVC project where we were dealing with Inherited controllers (SomeController was inherited by SomeMoreSpecificController) and we decided that it’d be nice to have a similar hierarchy of sharing and inheritance at the View level.
Unfortunately, out of the box, ASP.net MVC looks in two default locations for your views and [...]]]></description>
			<content:encoded><![CDATA[<p>I was working on a small MVC project where we were dealing with Inherited controllers (SomeController was inherited by SomeMoreSpecificController) and we decided that it’d be nice to have a similar hierarchy of sharing and inheritance at the View level.</p>
<p>Unfortunately, out of the box, ASP.net MVC looks in two default locations for your views and partials by convention.&#160; The first is /Views/ControllerName/ViewName.aspx, the second is /Views/Shared/ViewName.aspx.&#160; We wanted to allow SomeController to have it’s own set of more generic views, that could later be overridden in special cases by the views provided by SomeMoreSpecificController.</p>
<p>In order to do this in ASP.net MVC, you need to override the default view engine to change the location that the runtime looks for your views.</p>
<p>“Here’s one I made earlier”.</p>
<p>Using this ViewEngine, if you call an action method on SomeMoreSpecificController it’ll first check /Views/SomeMoreSpecificController/…, then /Views/SomeController/… (the base class), then finally /Views/Shared, allowing you a little more control over the organisation of your views.</p>
<blockquote><p>using System.Collections.Generic;     <br />using System.Web.Mvc;      <br />using System;      <br />using System.Globalization;      <br />using System.Linq; </p>
<p>namespace MyMvc.Mvc     <br />{ </p>
<p>&#160;&#160;&#160; public class InheranceViewEngine : WebFormViewEngine     <br />&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyFormat = &quot;:ViewCacheEntry:{0}:{1}:{2}:{3}:&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyPrefixMaster = &quot;Master&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private const string CacheKeyPrefixView = &quot;View&quot;;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; private static readonly List&lt;string&gt; EmptyLocations= new List&lt;string&gt;(); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public InheranceViewEngine()     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationFormats = new[]      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/{1}/{0}.aspx&quot;, &quot;~/Views/{1}/{0}.ascx&quot;, &quot;~/Views/Shared/{0}.aspx&quot;,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;~/Views/Shared/{0}.ascx&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; };      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (controllerContext == null)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentNullException(&quot;controllerContext&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (String.IsNullOrEmpty(viewName))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new ArgumentException(&quot;viewName&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; List&lt;string&gt; viewLocationsSearched;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; List&lt;string&gt; masterLocationsSearched; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string controllerName = controllerContext.RouteData.GetRequiredString(&quot;controller&quot;);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string viewPath = GetPath(controllerContext, ViewLocationFormats, viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string masterPath = GetPath(controllerContext, MasterLocationFormats, masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) &amp;&amp; !String.IsNullOrEmpty(masterName)))     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; List&lt;string&gt; strArray;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (controllerContext == null) { throw new ArgumentNullException(&quot;controllerContext&quot;); }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (string.IsNullOrEmpty(partialViewName)) { throw new ArgumentException(&quot;Partial View Name is null or empty.&quot;, &quot;partialViewName&quot;); }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string requiredString = controllerContext.RouteData.GetRequiredString(&quot;controller&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string str2 = GetPath(controllerContext, PartialViewLocationFormats, partialViewName, requiredString, &quot;Partial&quot;, useCache, out strArray);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (string.IsNullOrEmpty(str2))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(strArray);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return new ViewEngineResult(CreatePartialView(controllerContext, str2), this);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPath(ControllerContext controllerContext, string[] locations, string name, string controllerName, string cacheKeyPrefix, bool useCache, out List&lt;string&gt; searchedLocations)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = EmptyLocations; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (String.IsNullOrEmpty(name))     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return String.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (locations == null || locations.Length == 0)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new InvalidOperationException();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; bool nameRepresentsPath = IsSpecificPath(name);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (useCache)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string result = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (result != null)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return result;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (nameRepresentsPath)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromGeneralName(controllerContext, locations, name, controllerName, cacheKey, ref searchedLocations);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name, string controllerName, string cacheKey, ref List&lt;string&gt; searchedLocations)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string result = String.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = new List&lt;string&gt;(); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for (int i = 0; i &lt; locations.Length; i++)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string virtualPath = String.Format(CultureInfo.InvariantCulture, locations[i], name, controllerName);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (FileExists(controllerContext, virtualPath))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = EmptyLocations;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; result = virtualPath;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return result;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations.Add(virtualPath);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromGeneralNameOfBaseTypes(controllerContext.Controller.GetType(), locations, name, controllerContext, cacheKey, result, ref searchedLocations);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPathFromGeneralNameOfBaseTypes(Type descendantType, string[] locations, string name, ControllerContext controllerContext, string cacheKey, string result, ref List&lt;string&gt; searchedLocations)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Type baseControllerType = descendantType;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (baseControllerType == null       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || !baseControllerType.Name.Contains(&quot;Controller&quot;)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; || baseControllerType.Name == &quot;Controller&quot;)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return result;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for (int i = 0;i &lt; locations.Length;i++)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string baseControllerName = baseControllerType.Name.Replace(&quot;Controller&quot;, &quot;&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string virtualPath = String.Format(CultureInfo.InvariantCulture, locations[i], name, baseControllerName); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!string.IsNullOrEmpty(virtualPath) &amp;&amp;     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; FileExists(controllerContext, virtualPath))      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = EmptyLocations;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; result = virtualPath;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return result;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations.Add(virtualPath);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return GetPathFromGeneralNameOfBaseTypes(baseControllerType.BaseType, locations, name, controllerContext,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cacheKey, result, ref searchedLocations);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string CreateCacheKey(string prefix, string name, string controllerName)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return String.Format(CultureInfo.InvariantCulture, CacheKeyFormat,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; GetType().AssemblyQualifiedName, prefix, name, controllerName);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref List&lt;string&gt; searchedLocations)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string result = name; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!FileExists(controllerContext, name))     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; result = String.Empty;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; searchedLocations = new List&lt;string&gt;{ name };      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return result;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; private static bool IsSpecificPath(string name)     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; char c = name[0];      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return (c == &#8216;~&#8217; || c == &#8216;/&#8217;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; }     <br />}</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/feed/</wfw:commentRss>
		</item>
		<item>
		<title>I don&#8217;t understand Bayonetta</title>
		<link>http://daviddevelops.co.uk/index.php/2010/01/10/i-dont-understand-bayonetta/</link>
		<comments>http://daviddevelops.co.uk/index.php/2010/01/10/i-dont-understand-bayonetta/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 12:11:25 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[games]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2010/01/10/i-dont-understand-bayonetta/</guid>
		<description><![CDATA[I keep seeing glowing reviews of Bayonetta.&#160; You might have seen it advertised, the game with the “witch” that looks like Sarah Palin, who uses her hair as both a weapon and her outfit, has guns on the heels of her shoes and features in a game that has a button for “dance / taunt”.&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>I keep seeing glowing reviews of Bayonetta.&#160; You might have seen it advertised, the game with the “witch” that looks like Sarah Palin, who uses her hair as both a weapon and her outfit, has guns on the heels of her shoes and features in a game that has a button for “dance / taunt”.&#160; This game has unanimously been receiving 100% or near reviews from practically every major game publication.</p>
<p>But you know what, I really don&#8217;t understand this game.&#160; For a little context, I play everything.&#160; The well reviewed stuff when it first releases, and then the mediocre games when the price drops to around £20. </p>
<p>But I&#8217;m just not interested in this game.&#160; The pre release hype is tacky, it looks terrible (I don&#8217;t mean the style, the style is reasonable but playing the demo, it looks noticeably rough around the edges) and the game play appears to be extremely repetitive. </p>
<p>I downloaded the demo and just didn&#8217;t quite get to it until the game released, gave it a shot and was mildly entertained at best, and bored with it&#8217;s &quot;not retro, just not sophisticated&quot; game play within minutes. </p>
<p>I just don&#8217;t understand the heaps of praise people are giving this game.&#160; Sex sells, and that aside (because actually the sexuality in the game is very comic-book like and gets annoying fast, like, during the duration of the demo fast), the game play appears to be simple, repetitive, and if it&#8217;s 16 hours long, probably outstays its welcome. </p>
<p>I picked up Wet recently, a game that sells itself on roughly the same premise of &quot;hot girl on a vendetta&quot; for £20 and found it repetitive, gratuitous but a fun 10 hour game.&#160; It wasn&#8217;t a good game, but it was cheap and short enough to execute what it was attempting effectively.&#160; It got panned.&#160; Bayonetta was made by an influential game designer but suffers all of the same flaws of the former game, whilst suffering an uncomfortable character premise.&#160; It&#8217;s been widely acclaimed.&#160; I just don&#8217;t understand why.&#160; I’d honestly pay no more than about £5 for this game, and I’d probably give up playing very quickly.</p>
<p>These impressions are obviously all based on the pre-release hype, marketing material and the (terribly named) &quot;First Climax&quot; demo. </p>
<p>A friend of mine quipped &quot;if I was playing Bayonetta and somebody walked in, I&#8217;d turn it off and pretend it was porn&quot;.&#160; I can&#8217;t help but agree with that sentiment.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2010/01/10/i-dont-understand-bayonetta/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Writing Presentable Code Pt.1 &#8211; Properties and Variables</title>
		<link>http://daviddevelops.co.uk/index.php/2009/11/04/writing-presentable-code-pt-1-properties-and-variables/</link>
		<comments>http://daviddevelops.co.uk/index.php/2009/11/04/writing-presentable-code-pt-1-properties-and-variables/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 22:19:00 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[Software patterns]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2009/11/04/writing-presentable-code-pt-1-properties-and-variables/</guid>
		<description><![CDATA[At work we’re currently discussing coding standards, specifically to synchronise development in two countries and keep the style consistent across the teams.&#160; You know, the usual stuff.&#160; 
When people start discussing coding standards, it quickly devolves into a religious debate and honestly, I think a lot of it comes down to personal preference.&#160; Because of [...]]]></description>
			<content:encoded><![CDATA[<p>At work we’re currently discussing coding standards, specifically to synchronise development in two countries and keep the style consistent across the teams.&#160; You know, the usual stuff.&#160; </p>
<p>When people start discussing coding standards, it quickly devolves into a religious debate and honestly, I think a lot of it comes down to personal preference.&#160; Because of that, I’m going to spend a post or two telling your why you’re wrong and why I wouldn’t take your code to dinner however much it offered to put out.&#160; Because clearly my way is the only right way!</p>
<p>Joking aside, I want to go into some detail on how I present and write my code, and hopefully explain why.&#160; It’s all going to be slanted towards (who do I think I’m kidding, it’s going to be in) C# so as ever, your mileage may vary with any advice you extrapolate.&#160; I’m going to start out by showing you some bad examples, attempt to explain why I think they’re bad, and offer my alternative.</p>
<h2>Properties and Variables</h2>
<p>The way you declare your properties and variables is seemingly insignificant, but if you get it wrong it trashes the readability of your code.&#160; Take this code sample for example:</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image.png" rel="lightbox"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image_thumb.png" width="688" height="722" /></a> </p>
<p>All I’ve done in the above screenshot is declare a few properties, a few instance variables and a constructor.&#160; And it looks awful and un-maintainable despite the lack of any significant code smell, all due to the manner in which I’ve declared the variables.&#160; It’s a laundry list of mistakes.</p>
<ol>
<li>Using field backed properties when an auto-property will suffice. </li>
<li>Defining auto-properties split across multiple lines for no explicable reason. </li>
<li>Adding utterly redundant code comments (the code-criticism comments aside). </li>
<li>Terrible and ambiguous variable naming. </li>
<li>Variable names that contain hints at data types. </li>
</ol>
<p>The above code sample is practically unreadable, even without the comments, it’s long winded and obtuse:</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image1.png" rel="lightbox"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image_thumb1.png" width="476" height="860" /></a> </p>
</p>
<p>Now, I come from the school of thinking that is pretty much convinced that typing things is bad, repeating yourself is bad, hell, writing code is bad.&#160; So don’t.&#160; Less really is more, pick your favourite buzz phrase.&#160; Cleaning up your code should involve making it as simple and as clear as is humanly possible.</p>
<p>Thankfully, if you take advantage of the language features of C#3, you can quickly make something like that look like this:</p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image2.png" rel="lightbox"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image_thumb2.png" width="533" height="522" /></a> </p>
<p>Just by tidying up the way you declare and use your variables, you can make your code eminently more readable.&#160; If you compare the two examples, you’ll see that all I’ve done is</p>
<h3>Use single line declarations for auto-properties.</h3>
<ul>
<li>Why waste 3-5 lines on an auto-property that can easily fit one one without any loss in readability.      </li>
</ul>
<h3>Removed data backed properties in exchange for auto-properties with access modifiers on the setter.</h3>
<ul>
<li>Functionally equivalent and far neater      </li>
</ul>
<h3>Renamed badly named properties (in the first example “FLineOfAddress”) to be more meaningful.</h3>
<ul>
<li>Remove abbreviations where possible, they damage readability </li>
<li>Assume the maintainer of your code has no business knowledge, make things easy </li>
<li>Meaning is always better in variable names than in comments / meta-data </li>
<li>Don’t fear long variable names, modern IDEs have auto-complete, you don’t have to type that stuff by hand.&#160; Embrace your tooling! </li>
<li>If you can’t tell what’s in your property or variable from it’s name, you’ve failed, go back and try again.
<ul>
<li>This honestly includes stuff like foreach(var item in MyCollection) and StringBuilder sb = new StringBuilder();&#160; Both bad and wrong, don’t do it.          </li>
</ul>
</li>
</ul>
<h3>Only retained comments where the comment data is truly meaningful.&#160; </h3>
<ul>
<li>The above example isn’t particularly good (everyone knows what a URL is), but only keep comments in your code where they add something that you couldn’t attain with careful renaming and code restructuring / refactoring.&#160; The meaning of your code should be obvious to the reader without metadata.      </li>
</ul>
<h3>Stick to a solid naming convention for public / private / protected variables and properties.</h3>
<ul>
<li>The well trodden convention I’m following above is lowerCamelCase plus…
<ul>
<li>A leading underscore for private instance variables (determining scope) </li>
<li>Regular lowerCamelCase for local variables </li>
<li>UpperCamelCase for property names, constants and statics. </li>
<li>No data types in your variable names.&#160; This is not 1980. The IDE gives you all that lovely meta-data, don’t give yourself RSI duplicating it in your variable names.          </li>
</ul>
</li>
</ul>
<h3>Cleaning up usage</h3>
<ul>
<li>Removing this., you get the same scoping from using _ by convention in your variable names, save those fingers from RSI… </li>
<li>Using instance and local variables instantly becomes clearer by sticking to convention.      </li>
</ul>
<h3>Using var to reduce duplication in code.</h3>
<p> This is often controversial but I feel that using var, for the most part, reduces the amount of typing required without any loss of clarity.&#160; Take the following examples:   </p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image3.png" rel="lightbox"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image_thumb3.png" width="387" height="133" /></a>   <br />It’s clear to me that no clarity is lost by not typing &quot;StringBuilder” twice.&#160; It’s still right there in front of you and allows you to keep your variable declarations more uniform.&#160; Despite popular misconception this doesn’t affect the type safety of C#, the language and your variable are still strongly typed, the compiler just infers that when you said var you meant StringBuilder at compile time.&#160;&#160; If it isn’t really obvious what an object is when you instantiate it, you’re probably doing something really wrong elsewhere.   </p>
<p>People occasionally like to argue that while for declarations var is all well and good, when you’re using it for return values it causes a loss of clarity.&#160; It’s an interesting point but always feels slightly off the mark to me.&#160; Whenever people attempt to give me an example of this lack of clarity, it’s always that their variables or properties are ambiguously or inappropriately named, and the code clarity can be regained and even improved by naming the variables involved in a more descriptive way.&#160; Take the following snippet for example:   </p>
<p><a href="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image4.png" rel="lightbox"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://www.davidwhitney.co.uk/content/blog/wp-content/uploads/2009/11/image_thumb4.png" width="345" height="210" /></a>   <br />In the first case, I’d agree that using a var called “l” to store the return value of that method would lead to a loss in clarity.&#160; But if you had string l = RetrieveTextLabel(); and then, say, 20 lines down attempted to use a variable called “l” you’d probably deserve a swift kicking for naming something so poorly.&#160; By contrast, var textLabel is exceptionally descriptive.&#160; People also occasionally say that using var in foreach loops causes this ambiguity, but again, if you name your collection appropriately and your yeilded value correctly, it really is never an issue.   </p>
<p>Even more importantly, if you get your naming right, var actually helps you quickly refactor your code.&#160; As long as you understand the “meaning” of your variables, the IDE can fill in the blanks with regard to data types, because for the most part, it really doesn’t matter what type of data is actually in that variable when you’re reading the code as long as it’s meaning is a known quantity.&#160; I actually feel that the dynamic language crowd learnt this lesson long ago, and people that work predominantly in strongly typed languages actually tend to rely on the type system like a crutch to excuse terrible naming conventions.&#160; Time to learn from PHP…
<ol></ol>
<h3>In conclusion…</h3>
<p>To make your code readable you should stick to conventions for naming, always strive to add meaning in variable names and be as brief as possible.&#160; Don’t litter your code with crap and you’ll be thankful for it later.</p>
<p>Obviously, this is all my opinion, but I swear by it.</p>
<p>I’ll be following up this post in the next few days with some continued patterns for readable code.</p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2009/11/04/writing-presentable-code-pt-1-properties-and-variables/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Reusable Editable Fields for ASP.net MVC Using jQuery</title>
		<link>http://daviddevelops.co.uk/index.php/2009/10/08/reusable-editable-fields-for-asp-net-mvc-using-jquery/</link>
		<comments>http://daviddevelops.co.uk/index.php/2009/10/08/reusable-editable-fields-for-asp-net-mvc-using-jquery/#comments</comments>
		<pubDate>Thu, 08 Oct 2009 16:03:30 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[C#]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Software patterns]]></category>

		<category><![CDATA[free software]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2009/10/08/reusable-editable-fields-for-asp-net-mvc-using-jquery/</guid>
		<description><![CDATA[A friend recently asked me about editing items inline using ASP.net MVC, the kind of thing that was auto magically wired up with post backs in “old fashioned” asp.net so I’ve whipped up a small example showing how you can use jQuery to declaratively set up interactive field editing with a sprinkling of Ajax and [...]]]></description>
			<content:encoded><![CDATA[<p>A friend recently asked me about editing items inline using ASP.net MVC, the kind of thing that was auto magically wired up with post backs in “old fashioned” asp.net so I’ve whipped up a small example showing how you can use jQuery to declaratively set up interactive field editing with a sprinkling of Ajax and JSON.</p>
<p>I’m basing this example on the default ASP.net MVC starter project for brevity (download attached) but here’s an overview:</p>
<p>First you need to set up an Action method (or multiple action methods) on your controller to accept the modification of data.&#160; In my example I’ve added an unimaginative method called “SetField” to the HomeController that looks like this:</p>
<blockquote><p>public ActionResult SetField(string fieldName, string fieldValue)     <br />{      <br />&#160;&#160;&#160; var response = Json(fieldValue);      <br />&#160;&#160;&#160; return response;      <br />}</p>
</blockquote>
<p>As you can see, it doesn’t do very much (useful implementation left to the reader) but it accepts the parameters of a field name, and a field value.&#160; You’ll need to roll your own validation and sanity checking here.&#160; It then returns the fieldValue using the MVC Json helper object, as a Json object.&#160; In a real world example, you’d want to call and update in this method.</p>
<p>Now, in the view, jQuery does most of the hard work.</p>
<p>First I added a few CSS classes to the header on the master page (for the sake of example):</p>
<blockquote><p>&lt;style type=&quot;text/css&quot;&gt;     <br />&#160;&#160;&#160; .editableItem { display: block; }      <br />&#160;&#160;&#160; .fieldViewer { display: block; }      <br />&#160;&#160;&#160; .fieldEditor { display: none; }      <br />&#160;&#160;&#160; .editableItemCancel { display: block; }      <br />&#160;&#160;&#160; .editableItemBox { display: block; }      <br />&lt;/style&gt;</p>
</blockquote>
<p>I then added an example to the view that looked like this:</p>
<blockquote><p>&lt;div class=&quot;editableItem&quot; id=&quot;editable_FieldName&quot;&gt;     <br />&#160;&#160;&#160; &lt;div class=&quot;fieldViewer&quot;&gt;Click me to edit me!&lt;/div&gt;      <br />&#160;&#160;&#160; &lt;div class=&quot;fieldEditor&quot;&gt;&lt;input class=&quot;editableItemBox&quot; type=&quot;text&quot;/&gt;&lt;span class=&quot;editableItemCancel&quot;&gt;cancel&lt;/span&gt;&lt;/div&gt;      <br />&lt;/div&gt;</p>
</blockquote>
<p>With this HTML I set up some conventions that I’ll rely on when using jQuery.&#160; Firstly, every editable item should use the class “editableItem” and have the id “editable_FieldName”.&#160; I use the class in a jQuery selector and the Id to establish which field is being edited.&#160; Inside the editableItem should be a fieldViewer, containing the current data, and a fieldEditor, which is hidden by default, and contains some kind of editable controller and a cancel button.&#160; You could insert these elements at runtime if you wished, but in order to keep the example simple I’ve declared them in the HTML.</p>
<p>Next I added some jQuery… The jQuery defines some Javascript behaviour associated with the classes used in the HTML, this way, the mark-up can be reused to edit multiple fields rather than being keyed to the Id of a specific field.</p>
<blockquote><p>&lt;script src=&quot;/Scripts/jquery-1.3.2.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;     <br />&lt;script type=&quot;text/javascript&quot;&gt;      <br />&#160;&#160;&#160; jQuery(document).ready(function() { </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&quot;.editableItem .fieldViewer&quot;).click(function() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var parentId = $(this).parent().attr(&quot;id&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldEditor .editableItemBox&quot;).val($(&#8217;#&#8217; + parentId + &quot; .fieldViewer&quot;).text());      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldViewer&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldEditor&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&quot;.editableItem .fieldEditor .editableItemCancel&quot;).click(function() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var parentId = $(this).parent().parent().attr(&quot;id&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldViewer&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldEditor&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;.editableItem .editableItemBox&#8217;).keypress(function(e) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (e.which == 13) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var parentId = $(this).parent().parent().attr(&quot;id&quot;);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var fieldName = parentId.replace(/editable_/, &quot;&quot;); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $.post(&#8217;/Home/SetField&#8217;,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; fieldName: fieldName, fieldValue: $(&#8217;#&#8217; + parentId + &quot; .editableItemBox&quot;).val()      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; },      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; function(data) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldViewer&quot;).text(eval(&#8217;(&#8217; + data + &#8216;)&#8217;));      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldViewer&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; $(&#8217;#&#8217; + parentId + &quot; .fieldEditor&quot;).toggle();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; })      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }); </p>
<p>&#160;&#160;&#160; });      <br />&lt;/script&gt;</p>
</blockquote>
<p>Quite simply, if you click the editable field, it toggles into a textbox.&#160; If you hit enter on the textbox, the value is posted to the previously defined Action on the Controller.&#160; If you hit cancel, the display is toggled back.</p>
<p>I’d not recommend copy and pasting this exact example into a production system, but hopefully it’ll guide you through a simple scenario.&#160; You can use a similar technique to add all sorts of little Ajax tricks (auto-suggest, lookups, dynamic menus) to your ASP.net MVC site using jQuery and Json (both of which are included in the core asp.net MVC framework).</p>
<p><a href="http://www.davidwhitney.co.uk/software/repository/Examples/InteractiveEditingDemo.zip">Download the example solution here</a></p>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2009/10/08/reusable-editable-fields-for-asp-net-mvc-using-jquery/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Creating a WCF Proxy to talk to Magento</title>
		<link>http://daviddevelops.co.uk/index.php/2009/10/05/creating-a-wcf-proxy-to-talk-to-magento/</link>
		<comments>http://daviddevelops.co.uk/index.php/2009/10/05/creating-a-wcf-proxy-to-talk-to-magento/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 16:46:07 +0000</pubDate>
		<dc:creator>david</dc:creator>
		
		<category><![CDATA[.NET]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[WCF]]></category>

		<category><![CDATA[free software]]></category>

		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://www.davidwhitney.co.uk/content/blog/index.php/2009/10/05/creating-a-wcf-proxy-to-talk-to-magento/</guid>
		<description><![CDATA[I got a message from a friend who was struggling to do an integration piece with the Magento eCommerce Platform using the SOAP endpoint available at http://yourserver.co.uk/api/v2_soap?wsdl.
He brought an interesting problem to me, namely that the WCF svcutil executable (and built in Visual Studio 2008) was failing to generate any proxy code when supplied with [...]]]></description>
			<content:encoded><![CDATA[<p>I got a message from a friend who was struggling to do an integration piece with the <a href="http://www.magentocommerce.com/">Magento eCommerce Platform</a> using the SOAP endpoint available at <a title="http://yourserver.co.uk/api/v2_soap?wsdl" href="http://yourserver.co.uk/api/v2_soap?wsdl">http://yourserver.co.uk/api/v2_soap?wsdl</a>.</p>
<p>He brought an interesting problem to me, namely that the WCF svcutil executable (and built in Visual Studio 2008) was failing to generate any proxy code when supplied with a seemingly valid wsdl.</p>
<p>I did a quick test and managed to instantly reproduce the error.</p>
<p>Weirder still, when using the “old” .Net 2.0 add web reference method rather than the .Net 3.0+ “Add Service Reference”, the framework managed to create a non-WCF reference just fine.</p>
<p>Dropping down to the command line I saw some unusual messages being displayed by svcutil.exe:</p>
<blockquote><p>c:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin&gt;SvcUtil.exe <a href="http://yourdomain.co.uk/api/v2_soap?wsdl">http://yourserver.co.uk/api/v2_soap?wsdl</a>       <br />Attempting to download metadata from &#8216;<a href="http://yourdomain.co.uk/api/v2_soap?wsdl">http</a><a href="http://yourdomain.co.uk/api/v2_soap?wsdl">://yourserver.co.uk/api/v2_soap?wsdl</a>’ using WS-Metadata Exchange or DISCO.       </p>
<p>(Lots of error messages here…)</p>
<p>Error: Cannot import wsdl:portType      <br />Detail: An exception was thrown while running a WSDL import extension: System.Se       <br />rviceModel.Description.XmlSerializerMessageContractImporter       <br />Error: The &#8216; &#8216; character, hexadecimal value 0&#215;20, cannot be included in a name.       <br />Parameter name: name       <br />XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:Magento']/wsdl:p       <br />ortType[@name='Mage_Api_Model_Server_V2_HandlerPortType'] </p>
<p>Error: Cannot import wsdl:binding      <br />Detail: There was an error importing a wsdl:portType that the wsdl:binding is de       <br />pendent on.       <br />XPath to wsdl:portType: //wsdl:definitions[@targetNamespace='urn:Magento']/wsdl:       <br />portType[@name='Mage_Api_Model_Server_V2_HandlerPortType']       <br />XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:Magento']/wsdl:b       <br />inding[@name='Mage_Api_Model_Server_V2_HandlerBinding'] </p>
<p>Error: Cannot import wsdl:port      <br />Detail: There was an error importing a wsdl:binding that the wsdl:port is depend       <br />ent on.       <br />XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='urn:Magento']/wsdl:b       <br />inding[@name='Mage_Api_Model_Server_V2_HandlerBinding']       <br />XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:Magento']/wsdl:s       <br />ervice[@name='MagentoService']/wsdl:port[@name='Mage_Api_Model_Server_V2_Handler       <br />Port'] </p>
<p>Generating files&#8230;      <br />Warning: No code was generated.       <br />If you were trying to generate a client, this could be because the metadata docu       <br />ments did not contain any valid contracts or services       <br />or because all contracts/services were discovered to exist in /reference assembl       <br />ies. Verify that you passed all the metadata documents to the tool. </p>
<p>Warning: If you would like to generate data contracts from schemas make sure to      <br />use the /dataContractOnly option. </p>
<p>c:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin&gt;</p>
</blockquote>
<p>So I did a little digging through the WSDL and found an undocumented bug in Magneto’s schema.    </p>
<p>First I saved a local copy of the WSDL and used visual studio to reformat the document into some sort of readable state, then I had to make a few corrections to the WSDL to allow SvcUtil to correctly parse the malformed document.</p>
<p>Change 1:&#160; Replace a badly encoded apostrophe – I removed the “’s” from the following operation definition…</p>
<blockquote><p>&lt;operation name=&quot;customerGroupList&quot;&gt;      <br />&lt;documentation&gt;Retrieve customer’s groups&lt;/documentation&gt;       <br />&lt;input message=&quot;typens:customerGroupListRequest&quot;/&gt;       <br />&lt;output message=&quot;typens:customerGroupListResponse&quot;/&gt;       <br />&lt;/operation&gt;</p>
</blockquote>
<p>Change 2: Replace a trailing space in an operation name</p>
<blockquote><p>&lt;message name=&quot;catalogProductGetSpecialPriceRequest&quot;&gt;      <br />&#160; &lt;part name=&quot;sessionId&quot; type=&quot;xsd:string&quot;&gt;&lt;/part&gt;       <br />&#160; &lt;part name=&quot;product&quot; type=&quot;xsd:string&quot;&gt;&lt;/part&gt;       <br />&#160; &lt;part name=&quot;storeView &quot; type=&quot;xsd:string&quot;&gt;&lt;/part&gt;       <br />&lt;/message&gt;</p>
</blockquote>
<p>If you look carefully at the above message definition, you’ll notice that name=”storeView “ contains a space, making the wsdl invalid.&#160; Remove the space so it reads “storeView”.</p>
<p>With these two errors corrected, SvcUtil had no problem generating an appropriate WCF proxy from the corrected wsdl file.</p>
<p>Magneto will hopefully fix this error in the WSDL, but until this time, it’s probably quite safe to follow these steps to generate your own proxy.</p>
<p>To reproduce:</p>
<ul>
<li>Go to <a title="http://yourserver.co.uk/api/v2_soap?wsdl" href="http://yourserver.co.uk/api/v2_soap?wsdl">http://yourserver.co.uk/api/v2_soap?wsdl</a> and save the contents of your file to the local disk (c:\test\main.wsdl) </li>
<li>Open the file in visual studio, and reformat the document for readability (CTRL+K, CTRL+D). </li>
<li>Remove the apostrophe from the documentation tag for the customerGroupList operation. </li>
<li>Remove the space after the name=”storeView “ in the catalogProductGetSpecialPriceRequest message definition. </li>
<li>Open a command prompt and enter </li>
<li>c:\test&gt;c:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe main.wsdl </li>
<li>SvcUtil will produce two files, Magento.cs (your WCF proxy) and output.config, your endpoint configuration. </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://daviddevelops.co.uk/index.php/2009/10/05/creating-a-wcf-proxy-to-talk-to-magento/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
