<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;CEIMQ3w4eyp7ImA9WhFSFks.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988</id><updated>2013-06-19T13:23:02.233-04:00</updated><category term="asp.net" /><category term="routing" /><category term="mvccoderouting" /><category term="mvcpages" /><category term="asp.net web api" /><category term="plugin" /><category term="patterns" /><category term="html" /><category term="asp.net mvc" /><category term="asp.net web pages" /><title>Declarative Programming</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/maxtoroq" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="maxtoroq" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DEUGSH89cSp7ImA9WhBbGU4.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-327898979331089440</id><published>2013-05-18T22:59:00.000-04:00</published><updated>2013-05-19T00:03:49.169-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-19T00:03:49.169-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="routing" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>ASP.NET Routing: Regex Constraints are inneficient</title><content type="html">There are cases where using Regex constraints makes sense and does the job. For example, for action and controller parameters. If you have a constraint like &lt;code&gt;action = "Index|About|Contact"&lt;/code&gt;, that will prevent the handling, and URL generation, of non-existing actions. The framework only needs a string, from a list of valid values.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
But, if the route parameter will eventually map to a type different than string, like an enum or a numeric type, then you are&amp;nbsp;effectively parsing twice. For example, let's say we have a route parameter &lt;code&gt;foo&lt;/code&gt;, with a constraint&amp;nbsp;&lt;code&gt;\d+&lt;/code&gt;,&amp;nbsp;that maps to an action parameter &lt;code&gt;foo&lt;/code&gt; of type &lt;code&gt;Int32&lt;/code&gt;:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;If the route matches the constraints are processed (Regex parsing).&lt;/li&gt;
&lt;li&gt;If the constraint passes the string value of &lt;code&gt;foo&lt;/code&gt; is added to &lt;code&gt;RouteData&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Before action invocation, the string value of &lt;code&gt;foo&lt;/code&gt; is converted to &lt;code&gt;Int32&lt;/code&gt; using &lt;code&gt;System.Convert&lt;/code&gt;, which calls &lt;code&gt;Int32.Parse&lt;/code&gt; (Type parsing).&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Apart from the&amp;nbsp;unnecessary&amp;nbsp;double parsing, Regex parsing might not be 100% accurate and could match values that the Type parsing will later reject.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The process makes even less sense on URL generation:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;You pass an &lt;code&gt;Int32&lt;/code&gt; value for the &lt;code&gt;foo&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;Routing converts it to a string, to execute Regex parsing.&lt;/li&gt;
&lt;li&gt;If Regex matches the URL is generated.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
There's no need for Regex or any kind of parsing when I already have a value of the correct type. The problem is that the route doesn't know which type a route parameter maps to. To solve this, I came up with an &lt;code&gt;IRouteConstraint&lt;/code&gt; that works like this:&lt;/div&gt;
&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; Match(HttpContextBase httpContext, Route route, &lt;span class="kwrd"&gt;string&lt;/span&gt; parameterName, RouteValueDictionary values, RouteDirection routeDirection) {

   &lt;span class="kwrd"&gt;object&lt;/span&gt; originalVal = values[parameterName];

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (originalVal == &lt;span class="kwrd"&gt;null&lt;/span&gt;)
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.type.IsInstanceOfType(originalVal))
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;

   &lt;span class="kwrd"&gt;string&lt;/span&gt; stringVal = Convert.ToString(originalVal, InvariantFormatProvider);

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (stringVal.Length == 0)
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;

   &lt;span class="kwrd"&gt;object&lt;/span&gt; parsedVal;

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (!TryParse(stringVal, InvariantFormatProvider, &lt;span class="kwrd"&gt;out&lt;/span&gt; parsedVal))
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (routeDirection == RouteDirection.IncomingRequest)
      values[parameterName] = parsedVal;

   &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;
}&lt;/pre&gt;
What the above code does is:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;If the value is null or converts to an empty string then return true, which basically says ignore this constraint. If a route parameter is not optional then routing takes care of it by rejecting null or empty values.&lt;/li&gt;
&lt;li&gt;If the value is an instance of the type then return true, no need for further processing. This is usually true on URL generation and when using default values.&lt;/li&gt;
&lt;li&gt;If Type parsing is&amp;nbsp;successful&amp;nbsp;then return true, otherwise false. Also, for incoming requests, replace the string value with the parsed value. Because the &lt;code&gt;values&lt;/code&gt; parameter is an original dictionary and not a copy, this means that when &lt;code&gt;RouteDataValueProvider&lt;/code&gt; is asked for a&amp;nbsp;&lt;code&gt;foo&lt;/code&gt; parameter of type &lt;code&gt;Int32&lt;/code&gt; it will already have a value of that type and won't need to call &lt;code&gt;Int32.Parse&lt;/code&gt; again.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Check out &lt;a href="https://gist.github.com/maxtoroq/5606218"&gt;this gist&lt;/a&gt; for the complete code.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/327898979331089440/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2013/05/aspnet-routing-regex-constraints-are-inneficient.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/327898979331089440?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/327898979331089440?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2013/05/aspnet-routing-regex-constraints-are-inneficient.html" title="ASP.NET Routing: Regex Constraints are inneficient" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkYASH04eyp7ImA9WhBVE04.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-1203646694821899491</id><published>2013-04-18T20:04:00.000-03:00</published><updated>2013-04-18T22:15:49.333-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-18T22:15:49.333-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Delegate-based strongly-typed URL generation in ASP.NET MVC</title><content type="html">The standard way of generating URLs in ASP.NET MVC involves hard-coding action, controller and parameter names:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action(&lt;span class="str"&gt;"Edit"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = 5 });&lt;/pre&gt;
You are probably familiar with the expression tree-based strongly-typed URL generation approach, which is part of the &lt;a href="http://www.nuget.org/packages/Mvc4Futures"&gt;MvcFutures&lt;/a&gt; library. It allows you to write code like this:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action(() =&amp;gt; Edit(5));&lt;/pre&gt;
&lt;a name='more'&gt;&lt;/a&gt;Actually, MvcFutures does not extend &lt;code&gt;UrlHelper&lt;/code&gt;, but it exposes the low-level implementation methods so you can easily write the extension method yourself. If you wanted to link to an action in a different controller you could do it like this:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action&amp;lt;ProductController&amp;gt;(c =&amp;gt; c.Edit(5));&lt;/pre&gt;
The downside of this expression tree-based approach is that it's slow, two or three times slower than the standard approach.&lt;br /&gt;
&lt;h2&gt;
Delegate-based approach&lt;/h2&gt;
&lt;div&gt;
Instead of expression trees, why not just use delegates?&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action(Edit, 5);&lt;/pre&gt;
The above code looks even better than the expression tree-based example, and is as fast as the standard approach. The only downside of using delegates is that you need a non-null instance of the controller, which is not a problem when linking to other actions in the same controller, but to link to a different controller you would have to create an instance:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;var&lt;/span&gt; controller = &lt;span class="kwrd"&gt;new&lt;/span&gt; ProductController();
&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action(controller.Edit, 5);&lt;/pre&gt;
The instantiation of a controller could involve the execution of unneeded initialization code, for the purposes of just creating a URL. This is where we go back to needing expression trees. Perhaps we could use&amp;nbsp;&lt;a href="http://msdn.microsoft.com/library/system.runtime.serialization.formatterservices.getuninitializedobject"&gt;GetUninitializedObject&lt;/a&gt;, although I'm not sure how fast it is, and it doesn't work in medium trust.&lt;br /&gt;
&lt;br /&gt;
Still, when linking to other actions in the same controller this is my new favorite approach. And it works in views also, like this:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;var&lt;/span&gt; controller = (MyController)ViewContext.Controller;
&lt;span class="kwrd"&gt;string&lt;/span&gt; url = Url.Action(controller.Edit, 5);&lt;/pre&gt;
&lt;h2&gt;
Implementation&lt;/h2&gt;
The code is quite simple, don't let the overloads scare you. Generic type inference is key:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; StronglyTypedLinkExtensions {

   &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; ConcurrentDictionary&amp;lt;MethodInfo, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; actionNames = &lt;span class="kwrd"&gt;new&lt;/span&gt; ConcurrentDictionary&amp;lt;MethodInfo, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;ActionResult&amp;gt; action) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, &lt;span class="kwrd"&gt;default&lt;/span&gt;(&lt;span class="kwrd"&gt;object&lt;/span&gt;[]));
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T, ActionResult&amp;gt; action, T arg) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, ActionResult&amp;gt; action, T1 arg1, T2 arg2) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Action&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Func&amp;lt;T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ActionResult&amp;gt; action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; ActionImpl(urlHelper, action, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16);
   }

   &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ActionImpl(&lt;span class="kwrd"&gt;this&lt;/span&gt; UrlHelper urlHelper, Delegate action, &lt;span class="kwrd"&gt;params&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;[] args) {

      RouteValueDictionary values = &lt;span class="kwrd"&gt;null&lt;/span&gt;;

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (args != &lt;span class="kwrd"&gt;null&lt;/span&gt;
         &amp;amp;&amp;amp; args.Length &amp;gt; 0) {

         values = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteValueDictionary();

         &lt;span class="kwrd"&gt;var&lt;/span&gt; parameters = action.Method.GetParameters();

         &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; parameters.Length; i++)
            values[parameters[i].Name] = args[i]; 
      }

      &lt;span class="kwrd"&gt;string&lt;/span&gt; actionName = actionNames.GetOrAdd(action.Method, m =&amp;gt; {
            
         ActionNameAttribute attr = m
            .GetCustomAttributes(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ActionNameAttribute), inherit: &lt;span class="kwrd"&gt;true&lt;/span&gt;)
            .Cast&amp;lt;ActionNameAttribute&amp;gt;()
            .SingleOrDefault();

         &lt;span class="kwrd"&gt;return&lt;/span&gt; (attr != &lt;span class="kwrd"&gt;null&lt;/span&gt;) ?
            attr.Name
            : m.Name;
      });

      &lt;span class="kwrd"&gt;return&lt;/span&gt; urlHelper.Action(actionName, values);
   }
}&lt;/pre&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/1203646694821899491/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2013/04/delegate-based-strongly-typed-url.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1203646694821899491?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1203646694821899491?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2013/04/delegate-based-strongly-typed-url.html" title="Delegate-based strongly-typed URL generation in ASP.NET MVC" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;CEADRHgycSp7ImA9WhBWEk0.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-8102966186312324938</id><published>2013-03-29T01:36:00.001-03:00</published><updated>2013-04-05T19:59:35.699-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-05T19:59:35.699-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="routing" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net web api" /><title>Attribute-based routing coming to ASP.NET MVC and Web API v5</title><content type="html">Attribute-based routing is coming to ASP.NET MVC and Web API, according to the &lt;a href="http://aspnetwebstack.codeplex.com/wikipage?title=Roadmap&amp;amp;version=48"&gt;roadmap&lt;/a&gt;. They are basing the solution on the popular &lt;a href="http://attributerouting.net/"&gt;AttributeRouting&lt;/a&gt;&amp;nbsp;project. At the date of writing this post only Web API support is working, you can play with it by installing the &lt;a href="http://www.myget.org/gallery/aspnetwebstacknightly/"&gt;nightly packages&lt;/a&gt;.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
For a number of reasons I do not like the original project the ASP.NET team is basing on.&lt;br /&gt;
&lt;h2&gt;
Not very DRY&lt;/h2&gt;
&lt;div&gt;
There are many things that could be infered by the code, yet you have to be explicit about it. For instance:&lt;/div&gt;
&lt;pre&gt;[HttpGet("hello/{a:int}")]
public string Hello(int a) {
   return "Hi! " + a;
}
&lt;/pre&gt;
&lt;div&gt;
Why do I need to declare &lt;code&gt;int&lt;/code&gt; in the route, if I could just take that information from the method parameter?&amp;nbsp;&lt;/div&gt;
&lt;h2&gt;
No grouping&lt;/h2&gt;
Instead of grouping routes that are similar, all declared attribute routes map to a registered route. I don't like the idea of having &lt;a href="http://samsaffron.com/archive/2011/10/13/optimising-asp-net-mvc3-routing"&gt;650 routes like stackoverflow&lt;/a&gt;. It does not sound efficient. Can you imagine just looking at all those routes? What about matching the 650th route for URL generation? I bet stackoverflow hardcode URLs.&lt;br /&gt;
&lt;h2&gt;
No context&lt;/h2&gt;
&lt;div&gt;
Attribute routes are absolute, they do not take into account any information about where the action is located in the code, such as the controller, namespace, etc. This means the code that uses attribute routing is not portable.&lt;/div&gt;
&lt;h2&gt;
Conventional vs. Custom Routes&lt;/h2&gt;
&lt;div&gt;
The default route &lt;code&gt;{controller}/{action}/{id}&amp;nbsp;&lt;/code&gt;was not a bad start. You could develop a whole application without worrying about routing. Of course, at some point, the need for more customized routes arises. But that doesn't mean you should throw away conventions and use only custom routes. You just need a more flexible convention, and have the ability to do custom routes when needed. That's what &lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt; provides.&lt;br /&gt;
&lt;br /&gt;
The good thing is that, just like Areas, you can just ignore attribute routing.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/8102966186312324938/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2013/03/attribute-based-routing-aspnet-mvc.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8102966186312324938?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8102966186312324938?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2013/03/attribute-based-routing-aspnet-mvc.html" title="Attribute-based routing coming to ASP.NET MVC and Web API v5" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DUQERH09eSp7ImA9WhBWFE4.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-4635390956004291261</id><published>2013-02-08T19:50:00.000-03:00</published><updated>2013-04-08T13:08:25.361-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-08T13:08:25.361-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="routing" /><category scheme="http://www.blogger.com/atom/ns#" term="patterns" /><category scheme="http://www.blogger.com/atom/ns#" term="mvccoderouting" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Rethinking ASP.NET MVC: Workflow per Controller</title><content type="html">&lt;a href="http://stackoverflow.com/a/1528571/39923"&gt;My most controversial answer&lt;/a&gt; on StackOverflow is to a question about how to organize an ASP.NET MVC application. My answer was:&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote class="tr_bq"&gt;
Separating classes by category (Controllers, ViewModels, Filters etc.) is nonsense.&lt;br /&gt;
&lt;br /&gt;
If you want to write code for the Home section of your website (/) then create a folder named Home, and put there the HomeController, IndexViewModel, AboutViewModel, etc. and all related classes used by Home actions.&lt;br /&gt;
&lt;br /&gt;
If you have shared classes, like an ApplicationController, you can put it at the root of your project.&lt;br /&gt;
&lt;br /&gt;
Why separate things that are related (HomeController, IndexViewModel) and keep things together that have no relation at all (HomeController, AccountController) ?
&lt;/blockquote&gt;
This was downvoted for a long time. Eventually people started upvoting and now it's on the positive. People started to realize what a pain it is to maintain an app where the code for a single function is scattered all over the place. Categorization of information is useful if there's value in looking at all information of a certain category at once. For example, in Reflector you can look at all derived types of a certain type, which is super useful. Such function would be a great addition to Visual Studio. Back to MVC, if I could ask Visual Studio to show me all controllers, without having to put all controllers in the same directory/namespace, that would be great too. Because, when it comes to organizing code, keeping related types together is the best option for maintainability, specially of large codebases.&lt;br /&gt;
&lt;h2&gt;
Controllers and Namespaces&lt;/h2&gt;
So, for a long time I've been putting controllers and related view models in their own namespace. &lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt; makes it easy. But this lead to a new problem. Since, to take advantage of model validation and model metadata you have to write classes, it's natural in MVC to end up with a lot of classes. This lead to having namespaces with too many classes, many of these were unrelated view models. The problem started by having too many actions per controller. How many actions should a controller have? Some are going as far as having &lt;a href="http://jeffreypalermo.com/blog/the-asp-net-mvc-actioncontroller-ndash-the-controllerless-action-or-actionless-controller/"&gt;one action per controller&lt;/a&gt;. I wouldn't go that far. I think a controller should implement a single workflow, and that workflow should have its own namespace.&lt;br /&gt;
&lt;h2&gt;
Workflow per Controller&lt;/h2&gt;
&lt;div&gt;
So, what is a workflow? Let's take &lt;a href="http://mvcaccount.codeplex.com/"&gt;MvcAccount&lt;/a&gt; for example. There's a Reset Password function comprised of several actions:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;GET &lt;code&gt;/Account/ResetPassword&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;POST &lt;code&gt;/Account/ResetPassword&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/Account/PasswordResetVerificationSent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/Account/RP&lt;/code&gt; (by clicking on e-mail link)&lt;/li&gt;
&lt;li&gt;POST &lt;code&gt;/Account/RP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/Account/PasswordReset&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
To&amp;nbsp;successfully&amp;nbsp;reset a password you have go through this sequence of actions from start to finish. This is the Reset Password workflow, and should be represented in code by its own controller. This means going from:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MvcAccount {
   
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; AccountController : Controller {

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ResetPassword() { ... }

      [HttpPost]
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ResetPassword(ResetPasswordInput input) { ... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult PasswordResetVerificationSent() { ... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult RP(&lt;span class="kwrd"&gt;string&lt;/span&gt; id) { ... }

      [HttpPost]
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult RP(&lt;span class="kwrd"&gt;string&lt;/span&gt; id, FinishResetPasswordInput input) { ... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult PasswordReset() { ... }

      &lt;span class="rem"&gt;// ... lots of other unrelated actions below&lt;/span&gt;
   }
}&lt;/pre&gt;
To:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MvcAccount.Password.Reset {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ResetController : Controller {

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Reset() { ... }

      [HttpPost]
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Reset(ResetInput input) { ... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult VerificationSent() { ... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Finish(&lt;span class="kwrd"&gt;string&lt;/span&gt; id) { ... }

      [HttpPost]
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Finish(&lt;span class="kwrd"&gt;string&lt;/span&gt; id, FinishInput input) {... }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Done() { ... }
   }
}&lt;/pre&gt;
&lt;h3&gt;
Pros&lt;/h3&gt;
&lt;div&gt;
&lt;b&gt;Codebase neatly organized&lt;/b&gt;: No more namespaces with too many classes. All of the Reset Password workflow classes, which includes the controller and view models, sit in the &lt;code&gt;MvcAccount.Password.Reset&lt;/code&gt; namespace. The parent namespace, &lt;code&gt;MvcAccount.Password&lt;/code&gt;, can also be used to group related workflows, e.g. &lt;code&gt;MvcAccount.Password.Change&lt;/code&gt;. These can be considered related workflows because: a) they act on the same resource, or b) they share some infrastructure code (e.g. data access).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Smaller controllers&lt;/b&gt;: yep.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Shorter names of actions and view models&lt;/b&gt;: Namespaces gives us qualification and frees us from naming conflicts. &lt;code&gt;PasswordResetVerificationSent&lt;/code&gt; becomes &lt;code&gt;VerificationSent&lt;/code&gt;, and &lt;code&gt;FinishResetPasswordInput&lt;/code&gt; becomes &lt;code&gt;FinishInput&lt;/code&gt;. This also means shorter names for views.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Simpler dependency injection&lt;/b&gt;: Controller can take&amp;nbsp;dependencies&amp;nbsp;required by the workflow only.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;If implementation is simple enough you can move it to the controller class or workflow namespace&lt;/b&gt;: I won't get into a religious war about where the service or domain layer should go, whether it should be separate from the web layer, etc. etc., but since the controller is a lot smaller, and the workflow namespace only has a few classes, then there's a lot more you can put there.&lt;br /&gt;
&lt;h3&gt;
Cons&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Can produce longer routes&lt;/b&gt;: In some cases, it won't change much. For example, using MvcCodeRouting &lt;code&gt;/Account/PasswordResetVerificationSent&lt;/code&gt;&amp;nbsp;becomes&amp;nbsp;&lt;code&gt;/Account/Password/Reset/VerificationSent&lt;/code&gt;. But &lt;code&gt;/Account/ResetPassword&lt;/code&gt; becomes &lt;code&gt;/Account/Password/Reset/Reset&lt;/code&gt;. Why Reset twice? First Reset for the controller, second for the action. This can be avoided by changing the action to &lt;code&gt;Index&lt;/code&gt;, but I don't think that's a good name considering what the action does. Perhaps having a second convention for default actions, one where the action name is the same as the controller name, would help. In any case, when conventional routes are too long you can always resort to custom routes. &lt;span style="background-color: white;"&gt;&lt;b&gt;Update:&lt;/b&gt; MvcCodeRouting v1.1 supports custom default actions.&lt;/span&gt;&lt;br /&gt;
&lt;h2&gt;
How to avoid going back&lt;/h2&gt;
&lt;b&gt;Don't let your routing solution dictate how to organize your code&lt;/b&gt;. This was the mistake I made over and over. First I thought about the URL I wanted to implement, then I thought where in the project I had to put the code to implement that URL. This is how you end up with messy codebases. Always think first about the proper way to organize your code, then worry about routing and URLs.&lt;br /&gt;
&lt;h2&gt;
Show me the code&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Before: &lt;a href="https://mvcaccount.codeplex.com/SourceControl/BrowseLatest"&gt;https://mvcaccount.codeplex.com/SourceControl/BrowseLatest&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
After:&amp;nbsp;&lt;a href="https://github.com/maxtoroq/MvcAccount"&gt;https://github.com/maxtoroq/MvcAccount&lt;/a&gt;&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/4635390956004291261/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2013/02/aspnet-mvc-workflow-per-controller.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/4635390956004291261?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/4635390956004291261?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2013/02/aspnet-mvc-workflow-per-controller.html" title="Rethinking ASP.NET MVC: Workflow per Controller" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;DU8BRX06fCp7ImA9WhBTF0o.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-1699025452374234237</id><published>2013-01-12T13:50:00.000-03:00</published><updated>2013-02-13T14:50:54.314-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-13T14:50:54.314-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="patterns" /><title>My life is an anti-pattern</title><content type="html">For some reason people often look for THE solution to a particular problem, ignoring the fact that the best solution to any problem depends on its inputs. In other words, if someone asks you something there are two answers that are always correct: &lt;i&gt;I don't know&lt;/i&gt; and &lt;i&gt;it depends&lt;/i&gt;. These phrases probably won't help you much if you are a business man of a politician, but as programmers we should feel comfortable saying, specially the latter. Because, that IS what we do, right? Take some input, program some logic and return a result.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Yet, there are ideas some programmers put forward as THE right solution, ALWAYS. Meaning, if you're not doing it that way your code is inferior. Things like best practices, design principles, design patterns, and the worst of all, rules of thumb. The most important argument in favor of these ideas is that they are proven to work. Well, except for rules of thumb, which are just designed to work most of the time.&lt;br /&gt;
&lt;h2&gt;
Proven&lt;/h2&gt;
So, if something is proven it should be useful, right? It depends on your particular problem. Just because ORM, Repository, Unit of Work, LINQ to X are proven it doesn't mean they are good choices for a high traffic website, for example. So, it's the degree of utility, what you gain vs. what you lose. Proven means it works, it doesn't mean it's exactly what you need.&lt;br /&gt;
&lt;h2&gt;
Anti-patterns&lt;/h2&gt;
Not following a pattern, like fat models, thin controllers, session per request, means I'm following an anti-pattern? Of course not. It doesn't mean you are against (anti) that particular pattern, or that what you are doing instead is therefore wrong. This dualism is very common in our society: if you are not a capitalist you must be a communist, if you are not a republican you must be a democrat, and so on. It's a way for people to defend their beliefs by avoiding any reasoning. I'm not against patterns, all I'm saying is IT DEPENDS. Pattern advocates should not feel the need&amp;nbsp;to defend their beliefs by calling the opposite or absence of their pattern an anti-pattern.&lt;br /&gt;
&lt;h2&gt;
Endless &lt;i&gt;What if&lt;/i&gt;s&lt;/h2&gt;
&lt;div&gt;
What if some day you want to change ORM, or database server, or logging library? Don't use that static helper, what if you need to test that method? What if? What if?...&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Well, when that day comes I'll worry about that. If it isn't a requirement today, &lt;a href="http://en.wikipedia.org/wiki/You_ain%27t_gonna_need_it"&gt;YAGNI&lt;/a&gt;. Software development, specially application development, shouldn't be so focused on design decisions or making the code as politically correct as it can be. In fact, following too many patterns, specially abstraction patterns, can make your code perform worse, harder to understand, harder to maintain, etc.&amp;nbsp;&lt;a href="http://www.codinghorror.com/blog/2007/07/rethinking-design-patterns.html"&gt;Design patterns are a form of complexity&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
All problems are unique&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
My advice to you, dear reader, is don't think about a &lt;i&gt;type&lt;/i&gt; of problem, focus on what makes your problem unique, and you'll end up with the best solution.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/1699025452374234237/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2013/01/my-life-is-anti-pattern.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1699025452374234237?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1699025452374234237?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2013/01/my-life-is-anti-pattern.html" title="My life is an anti-pattern" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DUIFR3k-cSp7ImA9WhBUEkU.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-59800688205437215</id><published>2012-11-01T02:11:00.003-03:00</published><updated>2013-04-29T22:05:16.759-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-29T22:05:16.759-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mvcpages" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net web pages" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>MvcPages: ASP.NET MVC without routes and controllers</title><content type="html">I like developing with ASP.NET MVC, but sometimes I feel there are too many pieces involved. To develop a single function you have to (in no particular order):&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Create route (make sure it doesn't break other routes)&lt;/li&gt;
&lt;li&gt;Create controller/action&lt;/li&gt;
&lt;li&gt;Create view model&lt;/li&gt;
&lt;li&gt;Create view&lt;/li&gt;
&lt;/ul&gt;
... in addition to writing the model/service that does the actual work. Even though this layering gives you a lot of flexibility and testability, sometimes you don't need it. Do you really need all of the above to develop a simple contact form? Do you ever test your controllers/views? Do you have a lot of actions that just &lt;code&gt;return View()&lt;/code&gt;?&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
ASP.NET Web Pages&lt;/h2&gt;
Web Pages has the simplicity I sometimes want, except you lose a lot of power. With&amp;nbsp;Web Pages everything is parameter-based, there's no need to ever write a single class. MVC also has support for&amp;nbsp;parameter-based&amp;nbsp;approach&amp;nbsp;(action method parameter binding, ViewData/ViewBag), but the really nice features such as attribute-based validation and forms generation (EditorForModel) work with classes. These are the features I'm not willing to give up.&lt;br /&gt;
&lt;h2&gt;
MvcPages&lt;/h2&gt;
&lt;div&gt;
Forget about routes, forget about controllers, keep everything else. That's &lt;a href="https://github.com/maxtoroq/MvcPages"&gt;MvcPages&lt;/a&gt;, a very small library with no original features, just a base page class that allows you to access MVC features directly from standalone Razor pages. For example, this is a standard Web Pages page:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@{&lt;/span&gt;
   &lt;span class="kwrd"&gt;if&lt;/span&gt; (!User.Identity.IsAuthenticated) {
      FormsAuthentication.RedirectToLoginPage();
      &lt;span class="kwrd"&gt;return&lt;/span&gt;;
   }

   Validation.RequireField(&lt;span class="str"&gt;"currentPassword"&lt;/span&gt;, &lt;span class="str"&gt;"The current password field is required."&lt;/span&gt;);
   Validation.RequireField(&lt;span class="str"&gt;"newPassword"&lt;/span&gt;, &lt;span class="str"&gt;"The new password field is required."&lt;/span&gt;);
   
   Validation.Add(&lt;span class="str"&gt;"newPassword"&lt;/span&gt;,
      Validator.StringLength(
         maxLength: 100,
         minLength: 6,
         errorMessage: &lt;span class="str"&gt;"New password must be at least 6 characters"&lt;/span&gt;));
         
   Validation.Add(&lt;span class="str"&gt;"confirmPassword"&lt;/span&gt;,
      Validator.Required(&lt;span class="str"&gt;"The confirm new password field is required."&lt;/span&gt;),
      Validator.EqualsTo(&lt;span class="str"&gt;"newPassword"&lt;/span&gt;, &lt;span class="str"&gt;"The new password and confirmation password do not match."&lt;/span&gt;));

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsPost
      &amp;amp;&amp;amp; Validation.IsValid()) {

      &lt;span class="kwrd"&gt;string&lt;/span&gt; currentPassword = Request.Form[&lt;span class="str"&gt;"currentPassword"&lt;/span&gt;];
      &lt;span class="kwrd"&gt;string&lt;/span&gt; newPassword = Request.Form[&lt;span class="str"&gt;"newPassword"&lt;/span&gt;];
      &lt;span class="kwrd"&gt;string&lt;/span&gt; confirmPassword = Request.Form[&lt;span class="str"&gt;"confirmPassword"&lt;/span&gt;];

      &lt;span class="kwrd"&gt;bool&lt;/span&gt; passwordChanged = &lt;span class="kwrd"&gt;true&lt;/span&gt;; &lt;span class="rem"&gt;/* code that changes password here */&lt;/span&gt;

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (passwordChanged) {
         Response.Redirect(&lt;span class="str"&gt;"ChangePasswordSuccess"&lt;/span&gt;);
         &lt;span class="kwrd"&gt;return&lt;/span&gt;;
      } &lt;span class="kwrd"&gt;else&lt;/span&gt; {
         ModelState.AddFormError(&lt;span class="str"&gt;"An error occurred when attempting to change the password. Please contact the site owner."&lt;/span&gt;);
      }
   }

   Page.Title = &lt;span class="str"&gt;"Change Password"&lt;/span&gt;;

   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;span class="asp"&gt;@&lt;/span&gt;Page.Title&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="asp"&gt;@&lt;/span&gt;Html.ValidationSummary("Password change was unsuccessful. Please correct the errors and try again.", true)
   
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;method&lt;/span&gt;&lt;span class="kwrd"&gt;="post"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-label"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt; &lt;span class="attr"&gt;for&lt;/span&gt;&lt;span class="kwrd"&gt;="currentPassword"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Current password&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-field"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="currentPassword"&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="currentPassword"&lt;/span&gt; &lt;span class="asp"&gt;@&lt;/span&gt;Validation.For(&lt;span class="str"&gt;"currentPassword"&lt;/span&gt;)&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-label"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt; &lt;span class="attr"&gt;for&lt;/span&gt;&lt;span class="kwrd"&gt;="newPassword"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;New password&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-field"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="newPassword"&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="newPassword"&lt;/span&gt; &lt;span class="asp"&gt;@&lt;/span&gt;Validation.For(&lt;span class="str"&gt;"newPassword"&lt;/span&gt;)&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-label"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt; &lt;span class="attr"&gt;for&lt;/span&gt;&lt;span class="kwrd"&gt;="confirmPassword"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Confirm new password&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;label&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="editor-field"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="confirmPassword"&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="confirmPassword"&lt;/span&gt; &lt;span class="asp"&gt;@&lt;/span&gt;Validation.For(&lt;span class="str"&gt;"confirmPassword"&lt;/span&gt;)&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Change Password&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="asp"&gt;}&lt;/span&gt;&lt;/pre&gt;
Ugly, long, terrible. This is the same page using MvcPages:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@&lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Samples.Account
&lt;span class="asp"&gt;@model &lt;/span&gt;ChangePasswordModel
&lt;span class="asp"&gt;@{&lt;/span&gt;
   &lt;span class="kwrd"&gt;var&lt;/span&gt; service = &lt;span class="kwrd"&gt;new&lt;/span&gt; AccountService(ModelState);
   
   &lt;span class="kwrd"&gt;if&lt;/span&gt; (!User.Identity.IsAuthenticated) {
      FormsAuthentication.RedirectToLoginPage();
      &lt;span class="kwrd"&gt;return&lt;/span&gt;;
   }

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsPost
      &amp;amp;&amp;amp; TryUpdateModel()
      &amp;amp;&amp;amp; service.ChangePassword(Model)) {
      
      Response.Redirect(&lt;span class="str"&gt;"ChangePasswordSuccess"&lt;/span&gt;);
      &lt;span class="kwrd"&gt;return&lt;/span&gt;;
   }

   ViewBag.Title = &lt;span class="str"&gt;"Change Password"&lt;/span&gt;;

   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;span class="asp"&gt;@&lt;/span&gt;ViewBag.Title&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="asp"&gt;@&lt;/span&gt;Html.ValidationSummary(&lt;span class="kwrd"&gt;true&lt;/span&gt;, &lt;span class="str"&gt;"Password change was unsuccessful. Please correct the errors and try again."&lt;/span&gt;)
   
   &lt;span class="kwrd"&gt;using&lt;/span&gt; (Html.BeginForm()) {
      &lt;span class="asp"&gt;@&lt;/span&gt;Html.EditorForModel()
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Change Password&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   }
&lt;span class="asp"&gt;}&lt;/span&gt;&lt;/pre&gt;
Same ol' MVC. You know the features:&amp;nbsp;model binding, model validation, strongly-typed HTML helpers, editor and display templates, TempData, etc.&lt;br /&gt;
&lt;br /&gt;
Also, a new feature: Layout compatibility. Ever tried using an MVC layout from Web Pages? you get:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: white;"&gt;
&lt;div style="color: red; font-family: Verdana; font-size: 18pt; font-weight: normal;"&gt;
Server Error in '/' Application.&lt;br /&gt;
&lt;hr color="silver" size="1" width="100%" /&gt;
&lt;/div&gt;
&lt;div style="color: maroon; font-family: Verdana; font-size: 14pt; font-weight: normal;"&gt;
&lt;i&gt;The view at '~/SomePage.cshtml' must derive from WebViewPage, or WebViewPage&amp;lt;TModel&amp;gt;.&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
With MvcPages it's possible. You can now add Web Pages to your MVC app and use the same layout as your views.&lt;br /&gt;
&lt;br /&gt;
Like it? &lt;a href="http://www.nuget.org/packages/MvcPages"&gt;NuGet it&lt;/a&gt;, or &lt;a href="https://github.com/maxtoroq/MvcPages"&gt;fork it&lt;/a&gt;, or whatever.</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/59800688205437215/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/11/mvcpages-aspnet-mvc-without-routes-and-controllers.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/59800688205437215?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/59800688205437215?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/11/mvcpages-aspnet-mvc-without-routes-and-controllers.html" title="MvcPages: ASP.NET MVC without routes and controllers" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>5</thr:total></entry><entry gd:etag="W/&quot;DU8CSHg9eip7ImA9WhBTF0o.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-2125967528537587905</id><published>2012-10-27T00:33:00.000-03:00</published><updated>2013-02-13T14:51:09.662-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-13T14:51:09.662-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="patterns" /><category scheme="http://www.blogger.com/atom/ns#" term="plugin" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Implementing a contact form plugin for ASP.NET MVC</title><content type="html">The purpose of this post is to demonstrate the patterns presented in the&amp;nbsp;&lt;i&gt;Patterns for ASP.NET MVC Plugins&amp;nbsp;&lt;/i&gt;series so far.&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html"&gt;Routes, Controllers and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html"&gt;View Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;Demo: Implementing a contact form plugin&lt;/i&gt;&lt;/li&gt;
&lt;/ol&gt;
I've chosen the &lt;i&gt;contact form&lt;/i&gt;&amp;nbsp;scenario because it's a very common requirement most developers are familiar with. Also, the implementation is short and straightforward. The idea is that you focus on the patterns rather than the actual functionality of the plugin. The same patterns can be used to implement more interesting plugins.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
Model&lt;/h2&gt;
&lt;div&gt;
The model for the plugin is a very simple one, only four fields that the user needs to enter.&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ContactInput {

   [Required]
   [StringLength(100)]
   [Display(Order = 1)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Name { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }

   [Required]
   [DataType(DataType.EmailAddress)]
   [StringLength(254)]
   [RegularExpression(&lt;span class="str"&gt;@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"&lt;/span&gt;)]
   [Display(Order = 2)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Email { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }

   [Required]
   [StringLength(254)]
   [Display(Order = 3)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Subject { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }

   [Required]
   [StringLength(2000)]
   [DataType(DataType.MultilineText)]
   [Display(Order = 4)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Message { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
}&lt;/pre&gt;
This is what the series call an &lt;i&gt;Input Model&lt;/i&gt;, which defines the data that an action takes as input.&lt;br /&gt;
&lt;br /&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-display-order"&gt;Set Display(Order) for all properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-inputmodel-naming"&gt;Name input models after the action method name plus the "Input" suffix&lt;/a&gt;&lt;br /&gt;I'm deviating from the pattern here since the action method that uses this input model is Index and not Contact. I'm only using Index to make the URL as short and friendly as possible, and of course for compatibility with the default route. I think ContactInput is a much better name, since Index doesn't mean anything in this case.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-inputmodel-virtual-properties"&gt;Use the virtual modifier on all input model properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-data-type-attr"&gt;Use the DataTypeAttribute when appropiate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Configuration&lt;/h2&gt;
&lt;div&gt;
The following is the class used to provide configuration settings for the plugin.&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ContactConfiguration {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; From { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; To { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; CC { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Bcc { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; Func&amp;lt;ContactSender&amp;gt; ContactSenderResolver { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
}&lt;/pre&gt;
&lt;code&gt;ContactSender&lt;/code&gt; is the class that does the work. &lt;code&gt;ContactSenderResolver&lt;/code&gt; is an optional setting used to provide a customized instance of &lt;code&gt;ContactSender&lt;/code&gt;, we'll be using it later.&lt;br /&gt;
&lt;br /&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-configuration-class"&gt;Provide a class for the plugin's configuration settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-configuration-resolver-properties"&gt;Provide Func&amp;lt;&amp;gt; properties for each dependency,  and name them with the "Resolver" suffix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Service&lt;/h2&gt;
&lt;div&gt;
The following is the definition of &lt;code&gt;ContactSender&lt;/code&gt; (some code ommited for clarity).&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ContactSender {

   &lt;span class="kwrd"&gt;readonly&lt;/span&gt; SmtpClient smtpClient;
   ContactConfiguration config;
      
   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactConfiguration Configuration {
      &lt;span class="kwrd"&gt;get&lt;/span&gt; { &lt;span class="kwrd"&gt;return&lt;/span&gt; config; }
      &lt;span class="kwrd"&gt;set&lt;/span&gt; { config = value; }
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactSender() 
      : &lt;span class="kwrd"&gt;this&lt;/span&gt;(&lt;span class="kwrd"&gt;new&lt;/span&gt; SmtpClient()) { }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactSender(SmtpClient smtpClient) {

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (smtpClient == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class="str"&gt;"smtpClient"&lt;/span&gt;);

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.smtpClient = smtpClient;
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; ContactInput CreateContactInput() {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ContactInput();
   }

   &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; InitializeContactInput(ContactInput input) { }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactInput Send() {

      ContactInput input = CreateContactInput();
      InitializeContactInput(input);

      &lt;span class="kwrd"&gt;return&lt;/span&gt; input;
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; Send(ContactInput input) {

      &lt;span class="kwrd"&gt;var&lt;/span&gt; message = &lt;span class="kwrd"&gt;new&lt;/span&gt; MailMessage {
         To = { &lt;span class="kwrd"&gt;this&lt;/span&gt;.config.To },
         ReplyToList = { &lt;span class="kwrd"&gt;new&lt;/span&gt; MailAddress(input.Email, input.Name) },
         Subject = input.Subject,
         Body = renderViewAsString(&lt;span class="str"&gt;"_MailHtml"&lt;/span&gt;, input)
      };

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.From != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
         message.From = &lt;span class="kwrd"&gt;new&lt;/span&gt; MailAddress(&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.From);

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.CC != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
         message.CC.Add(&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.CC);

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.Bcc != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
         message.Bcc.Add(&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.Bcc);

      &lt;span class="kwrd"&gt;try&lt;/span&gt; {
         &lt;span class="kwrd"&gt;this&lt;/span&gt;.smtpClient.Send(message);

      } &lt;span class="kwrd"&gt;catch&lt;/span&gt; (SmtpException ex) {
            
         LogException(ex);

         &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;
      }

      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;
   }
}&lt;/pre&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-inputmodel-create"&gt;Provide a virtual method for the creation of input models, and name it after the type name plus the "Create" prefix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-inputmodel-initialize"&gt;Provide a virtual method for the initialization of input models, and name it after the type name plus the "Initialize" prefix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
View Model&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; IndexViewModel {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactInput InputModel { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; IndexViewModel(ContactInput inputModel) {
      &lt;span class="kwrd"&gt;this&lt;/span&gt;.InputModel = inputModel;
   }
}&lt;/pre&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-use"&gt;Always use strongly-typed view models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-naming"&gt;Name view models after the view plus the "ViewModel" suffix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-separate-classes"&gt;Define separate classes for input and output models, plus a view model class that reference the other two&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
View&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@model &lt;/span&gt;IndexViewModel

&lt;span class="asp"&gt;@&lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; (Html.BeginForm()) { 
   &lt;span class="asp"&gt;@&lt;/span&gt;Html.AntiForgeryToken()
   &lt;span class="kwrd"&gt;var&lt;/span&gt; inputHtml = HtmlUtil.HtmlHelperFor(Html, Model.InputModel);
   &lt;span class="asp"&gt;@&lt;/span&gt;inputHtml.EditorForModel()

   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="submit"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
}&lt;/pre&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-htmlhelper-for-inputmodel"&gt;Create an HtmlHelper&amp;lt;TModel&amp;gt; instance for form rendering, where TModel is the input model type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Controller&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[OutputCache(Location = OutputCacheLocation.None)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ContactController : Controller {

   ContactConfiguration config;
   ContactSender service;

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactController() { }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; ContactController(ContactSender service) {
      &lt;span class="kwrd"&gt;this&lt;/span&gt;.service = service;
   }

   &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Initialize(RequestContext requestContext) {
         
      &lt;span class="kwrd"&gt;base&lt;/span&gt;.Initialize(requestContext);

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.config = requestContext.RouteData.DataTokens[&lt;span class="str"&gt;"Configuration"&lt;/span&gt;] &lt;span class="kwrd"&gt;as&lt;/span&gt; ContactConfiguration
         ?? &lt;span class="kwrd"&gt;new&lt;/span&gt; ContactConfiguration();

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.config.ContactSenderResolver != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
         &lt;span class="kwrd"&gt;this&lt;/span&gt;.service = &lt;span class="kwrd"&gt;this&lt;/span&gt;.config.ContactSenderResolver();

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.service == &lt;span class="kwrd"&gt;null&lt;/span&gt;)
         &lt;span class="kwrd"&gt;this&lt;/span&gt;.service = &lt;span class="kwrd"&gt;new&lt;/span&gt; ContactSender();

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.service.Configuration = &lt;span class="kwrd"&gt;this&lt;/span&gt;.config;
   }

   [HttpGet]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index() {

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.ViewData.Model = &lt;span class="kwrd"&gt;new&lt;/span&gt; IndexViewModel(&lt;span class="kwrd"&gt;this&lt;/span&gt;.service.Send());
         
      &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
   }

   [HttpPost]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index(&lt;span class="kwrd"&gt;string&lt;/span&gt; foo) {

      ContactInput input = &lt;span class="kwrd"&gt;this&lt;/span&gt;.service.CreateContactInput();

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.ViewData.Model = &lt;span class="kwrd"&gt;new&lt;/span&gt; IndexViewModel(input);
         
      &lt;span class="kwrd"&gt;if&lt;/span&gt; (!ModelBinderUtil.TryUpdateModel(input, &lt;span class="kwrd"&gt;this&lt;/span&gt;)) {
            
         &lt;span class="kwrd"&gt;this&lt;/span&gt;.Response.StatusCode = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)HttpStatusCode.BadRequest;
         &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
      }

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (!&lt;span class="kwrd"&gt;this&lt;/span&gt;.service.Send(input)) {
            
         &lt;span class="kwrd"&gt;this&lt;/span&gt;.Response.StatusCode = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)HttpStatusCode.InternalServerError;
         &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
      }

      &lt;span class="kwrd"&gt;return&lt;/span&gt; RedirectToAction(&lt;span class="str"&gt;"Success"&lt;/span&gt;);
   }

   [HttpGet]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Success() {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; View();
   }
}&lt;/pre&gt;
Patterns used:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-controllers-public"&gt;Make controllers public&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-controllers-common-naming"&gt;Name controllers with common, widely used names&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-controllers-minimize"&gt;Minimize the number of controllers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-controllers-default-constructor"&gt;Provide a default constructor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-controllers-constructor-dependencies"&gt;Provide constructors that take dependencies as arguments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#pattern-configuration-data-token"&gt;Use the 'Configuration' data token to accept an instance of the configuration class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-modelbinding-inheritance"&gt;Use a model binding implementation that supports subclassing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#pattern-viewmodels-modelbinding-failure-status-code"&gt;Set Response.StatusCode to 400 (Bad Request) if model binding fails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Using the plugin&lt;/h2&gt;
&lt;div&gt;
This is how you can register the plugin using &lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt;.&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterRoutes(RouteCollection routes) {
         
   routes.MapCodeRoutes(
      baseRoute: &lt;span class="str"&gt;"Contact"&lt;/span&gt;,
      rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ContactController),
      settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; CodeRoutingSettings {
         EnableEmbeddedViews = &lt;span class="kwrd"&gt;true&lt;/span&gt;,
         Configuration = &lt;span class="kwrd"&gt;new&lt;/span&gt; ContactConfiguration {
            To = &lt;span class="str"&gt;"contact@example.com"&lt;/span&gt;
         }
      }
   );
}&lt;/pre&gt;
If you visit /Contact you'll see the contact form.
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-dJWUTaLHgIc/UItB1BE-cvI/AAAAAAAAAqs/O7sez0FwG3w/s1600/fig3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" class="noborder" height="301" src="http://1.bp.blogspot.com/-dJWUTaLHgIc/UItB1BE-cvI/AAAAAAAAAqs/O7sez0FwG3w/s400/fig3.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
Adding a field to the form&lt;/h2&gt;
&lt;div&gt;
Let's add a second plugin instance, this time with an extra field. First, we need to inherit &lt;code&gt;ContactInput&lt;/code&gt; and add the new property.&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CustomContactInput : ContactInput {

   [Required]
   [Display(Name = &lt;span class="str"&gt;"How did you hear about us?"&lt;/span&gt;, Order = 3)]
   [UIHint(&lt;span class="str"&gt;"Source"&lt;/span&gt;)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Source { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
}&lt;/pre&gt;
Next, inherit &lt;code&gt;ContactSender&lt;/code&gt; and override &lt;code&gt;CreateContactInput&lt;/code&gt;.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CustomContactSender : ContactSender {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; ContactInput CreateContactInput() {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CustomContactInput();
   }
}&lt;/pre&gt;
Note the use of &lt;code&gt;UIHint("Source")&lt;/code&gt; on the new property, let's add that editor template.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@&lt;/span&gt;Html.DropDownList(&lt;span class="str"&gt;""&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"Friend"&lt;/span&gt;, &lt;span class="str"&gt;"Advertisement"&lt;/span&gt;, &lt;span class="str"&gt;"Google"&lt;/span&gt;, &lt;span class="str"&gt;"Other"&lt;/span&gt; }
   .Select(s =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SelectListItem { Text = s, Value = s }), &lt;span class="str"&gt;""&lt;/span&gt;)&lt;/pre&gt;
Lastly, we register this new plugin instance.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapCodeRoutes(
   baseRoute: &lt;span class="str"&gt;"CustomContact"&lt;/span&gt;,
   rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ContactController),
   settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; CodeRoutingSettings {
      EnableEmbeddedViews = &lt;span class="kwrd"&gt;true&lt;/span&gt;,
      Configuration = &lt;span class="kwrd"&gt;new&lt;/span&gt; ContactConfiguration {
         To = &lt;span class="str"&gt;"info@example.com"&lt;/span&gt;,
         ContactSenderResolver = () =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Models.CustomContactSender()
      }
   }
);&lt;/pre&gt;
Note I'm also using a different destination address (&lt;code&gt;To&lt;/code&gt; configuration setting). If you visit /CustomContact you can see the form with the new field.
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-A8x_V2n74cg/UItIWnsAbsI/AAAAAAAAAq8/k3bsjLHsjno/s1600/fig4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" class="noborder" height="301" src="http://2.bp.blogspot.com/-A8x_V2n74cg/UItIWnsAbsI/AAAAAAAAAq8/k3bsjLHsjno/s400/fig4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
Conclusions&lt;/h2&gt;
&lt;div&gt;
Hopefully seeing the patterns in action makes their utility more clear.&amp;nbsp;The goal is to provide a consistent experience for plugin consumers (application developers), minimize the amount of configuration required to get a plugin working in the host application and maximize the plugin's flexibility to customize it's behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://github.com/maxtoroq/apps.web.contact"&gt;Source code&lt;/a&gt;&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/2125967528537587905/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/10/implementing-contact-form-plugin-for-aspnet-mvc.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/2125967528537587905?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/2125967528537587905?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/10/implementing-contact-form-plugin-for-aspnet-mvc.html" title="Implementing a contact form plugin for ASP.NET MVC" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-dJWUTaLHgIc/UItB1BE-cvI/AAAAAAAAAqs/O7sez0FwG3w/s72-c/fig3.png" height="72" width="72" /><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;D0EMQn44fCp7ImA9WhNRGEg.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-9159506019263254704</id><published>2012-09-23T19:44:00.000-03:00</published><updated>2012-11-13T22:14:43.034-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T22:14:43.034-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="routing" /><category scheme="http://www.blogger.com/atom/ns#" term="mvccoderouting" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>5 reasons why you should use MvcCodeRouting</title><content type="html">&lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt; v1.0 is out, this post highlights the library's most important features and why I believe is a must have for all ASP.NET MVC developers.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
1. Convention over configuration&lt;/h2&gt;
&lt;div&gt;
Getting started with MvcCodeRouting in a new project, or integrating with an existing codebase, requires very little configuration, and many times no configuration at all. This is because the library recognizes that most of the time you follow the &lt;code&gt;{controller}/{action}&lt;/code&gt; or &lt;code&gt;{controller}/{action}/{id}&lt;/code&gt; convention. For example, let's take the &lt;a href="http://mvcmusicstore.codeplex.com/"&gt;MvcMusicStore&lt;/a&gt; application and use MvcCodeRouting on it:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterRoutes(RouteCollection routes)
{
   routes.IgnoreRoute(&lt;span class="str"&gt;"{resource}.axd/{*pathInfo}"&lt;/span&gt;);

   &lt;span class="rem"&gt;//routes.MapRoute(&lt;/span&gt;
   &lt;span class="rem"&gt;//    "Default", // Route name&lt;/span&gt;
   &lt;span class="rem"&gt;//    "{controller}/{action}/{id}", // URL with parameters&lt;/span&gt;
   &lt;span class="rem"&gt;//    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults&lt;/span&gt;
   &lt;span class="rem"&gt;//);&lt;/span&gt;

   routes.MapCodeRoutes(
      rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Controllers.HomeController),
      settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; CodeRoutingSettings {
         UseImplicitIdToken = &lt;span class="kwrd"&gt;true&lt;/span&gt;
      }
   );
}&lt;/pre&gt;
Calling &lt;code&gt;MapCodeRoutes&lt;/code&gt; is the only change required. You can build and run the application as if nothing changed. If you visit ~/routes.axd you can see the routes that MvcCodeRouting created:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.IgnoreRoute(&lt;span class="str"&gt;"{resource}.axd/{*pathInfo}"&lt;/span&gt;);

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Home"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"StoreManager/{action}/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"StoreManager"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Delete|Details|Edit"&lt;/span&gt;, id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Store/Details/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Store"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Details"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Checkout/Complete/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Checkout"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Complete"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"ShoppingCart/{action}/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"ShoppingCart"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"AddToCart|RemoveFromCart"&lt;/span&gt;, id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Account/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Account"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"LogOn|LogOff|Register|ChangePassword|ChangePasswordSuccess"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"StoreManager/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"StoreManager"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Index|Create|Edit"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Store/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Store"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Index|Browse|GenreMenu"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Checkout/AddressAndPayment"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Checkout"&lt;/span&gt;, action = &lt;span class="str"&gt;@"AddressAndPayment"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"ShoppingCart/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"ShoppingCart"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Index|CartSummary"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });
&lt;/pre&gt;
OK, so the application worked fine with 1 route and now we have 10, how is that better? Well, in some aspects these routes work better (more on that follows), but the point I was trying to make here is &lt;b&gt;how easy it is to start using MvcCodeRouting&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
MvcMusicStore is actually not a very good case study, because it only uses the default route (&lt;code&gt;{controller}/{action}/{id}&lt;/code&gt;). Routing presents no challenges when you only have one route, but real-world applications use more than one route, and that's when you start facing issues, since adding new routes can potentially break the existing ones.&lt;br /&gt;
&lt;h2&gt;
2. Automatic route grouping, ordering and&amp;nbsp;constraining&lt;/h2&gt;
&lt;div&gt;
Instead of creating one route per action MvcCodeRouting&amp;nbsp;&amp;nbsp;groups&amp;nbsp;similar actions to minimize the number of routes created. For instance, the routes for the &lt;code&gt;StoreManager&lt;/code&gt; controller are:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"StoreManager/{action}/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"StoreManager"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Delete|Details|Edit"&lt;/span&gt;, id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"StoreManager/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"StoreManager"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"Index|Create|Edit"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });
&lt;/pre&gt;
Since &lt;code&gt;Delete&lt;/code&gt;, &lt;code&gt;Details&lt;/code&gt; and &lt;code&gt;Edit&lt;/code&gt; actions take an id parameter of the same type (Int32) only one route is created that can handle those actions. Keeping the &lt;b&gt;number of routes to a minimum makes route matching more efficient&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Note that &lt;code&gt;Edit&lt;/code&gt; is also listed in the second route, because there's also an &lt;code&gt;Edit&lt;/code&gt; action that takes no parameters. The order of these routes affects URL generation. For example,&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;Url.Action("Edit", "StoreManager", new { id = 1 })&lt;/code&gt; returns &lt;code&gt;"/StoreManager/Edit/1"&lt;/code&gt;,&lt;br /&gt;
&lt;br /&gt;
but if you switch the order&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;Url.Action("Edit", "StoreManager", new { id = 1 })&lt;/code&gt; returns &lt;code&gt;"/StoreManager/Edit?id=1"&lt;/code&gt; (note id in query string).&lt;br /&gt;
&lt;br /&gt;
MvcCodeRouting knows &lt;b&gt;how to order routes to avoid URL generation issues&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Using &lt;b&gt;constraints for the action token avoids route conflicts&lt;/b&gt;.&amp;nbsp;
MvcCodeRouting&amp;nbsp;also uses constraints for action parameters, which you can override on a per-parameter or per-site basis. Constraining also helps &lt;b&gt;keeping bad URLs out&lt;/b&gt;, e.g. &lt;code&gt;/StoreManager/Edit/foo&lt;/code&gt;&amp;nbsp;doesn't match any route.&lt;br /&gt;
&lt;h2&gt;
3. Custom routes&lt;/h2&gt;
&lt;div&gt;
Now that we have conventional routes automatically created for us let's see how we can configure more customized routes. Let's change &lt;code&gt;Store/Details/{id}&lt;/code&gt; to a more SEO friendly format, like &lt;code&gt;p/{id}/{slug}&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
This is the current action code:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Details(&lt;span class="kwrd"&gt;int&lt;/span&gt; id)
{
   &lt;span class="kwrd"&gt;var&lt;/span&gt; album = storeDB.Albums.Find(id);

   &lt;span class="kwrd"&gt;return&lt;/span&gt; View(album);
}&lt;/pre&gt;
We'll change it to this:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;[CustomRoute(&lt;span class="str"&gt;"~/p/{id}/{slug}"&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Details([FromRoute]&lt;span class="kwrd"&gt;int&lt;/span&gt; id, [FromRoute]&lt;span class="kwrd"&gt;string&lt;/span&gt; slug = &lt;span class="kwrd"&gt;null&lt;/span&gt;)
{
   &lt;span class="kwrd"&gt;var&lt;/span&gt; album = storeDB.Albums.Find(id);

   &lt;span class="kwrd"&gt;if&lt;/span&gt; (album != &lt;span class="kwrd"&gt;null&lt;/span&gt;
      &amp;amp;&amp;amp; album.Title != slug) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; RedirectToAction(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { id, slug = album.Title });
   }

   &lt;span class="kwrd"&gt;return&lt;/span&gt; View(album);
}&lt;/pre&gt;
The use of the &lt;code&gt;CustomRoute&lt;/code&gt; attribute should be self explanatory. The &lt;code&gt;FromRoute&lt;/code&gt; attribute must be used on route parameters. Optional parameters are used to specify if the corresponding route parameter should be optional.&amp;nbsp;Since we don't have a slug property in our model I'm using Title for now.&lt;/div&gt;
&lt;br /&gt;
This is the resulting route definition:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"p/{id}/{slug}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Store"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Details"&lt;/span&gt;, slug = UrlParameter.Optional }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });&lt;/pre&gt;
As you can see, &lt;b&gt;custom routes are easy&lt;/b&gt;.&lt;br /&gt;
&lt;div&gt;
&lt;h2&gt;
4. Route formatting&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Now that we have SEO friendly routes let's see if we can make our conventional routes look prettier. A very common URL formatting convention is lower case hyphenated, this is how we can an implement it:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;routes.MapCodeRoutes(
   rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Controllers.HomeController),
   settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; CodeRoutingSettings { 
      UseImplicitIdToken = &lt;span class="kwrd"&gt;true&lt;/span&gt;,
      RouteFormatter = args =&amp;gt;
         Regex.Replace(args.OriginalSegment, &lt;span class="str"&gt;@"([a-z])([A-Z])"&lt;/span&gt;, &lt;span class="str"&gt;"$1-$2"&lt;/span&gt;)
            .ToLowerInvariant()
   }
);&lt;/pre&gt;
&lt;code&gt;RouteFormatter&lt;/code&gt; is a delegate that takes information about a route segment and returns a modified segment. Routes now look like this one:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"shopping-cart/{action}/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"ShoppingCart"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"add-to-cart|remove-from-cart"&lt;/span&gt;, id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers"&lt;/span&gt; });&lt;/pre&gt;
The beauty of route formatting is that it doesn't affect URL generation. For instance, &lt;code&gt;Url.Action("AddToCart", "ShoppingCart")&lt;/code&gt; continues to work, even though the route uses &lt;code&gt;add-to-cart&lt;/code&gt; and not &lt;code&gt;AddToCart&lt;/code&gt;. &lt;b&gt;Route formatting is easy&lt;/b&gt;.&lt;br /&gt;
&lt;h2&gt;
5. Namespaces&lt;/h2&gt;
&lt;div&gt;
Currently, the &lt;code&gt;StoreManager&lt;/code&gt; controller only manages &lt;code&gt;Album&lt;/code&gt;s, what about the other entities in our application, like &lt;code&gt;Artist&lt;/code&gt; and &lt;code&gt;Genre&lt;/code&gt;? Let's add actions for those too, but adding them to the &lt;code&gt;StoreManager&lt;/code&gt; controller would make it grow too much and make the code hard to maintain, so instead let's create separate controllers in a sub-namespace:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MvcMusicStore.Controllers.StoreManager &lt;span class="rem"&gt;// &amp;lt;-- Note the sub-namespace&lt;/span&gt;
{
   [Authorize(Roles = &lt;span class="str"&gt;"Administrator"&lt;/span&gt;)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ArtistController : Controller 
   {
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index() 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
      }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ViewResult Details(&lt;span class="kwrd"&gt;int&lt;/span&gt; id) 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();         
      }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ViewResult Create() 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
      }
   }

   [Authorize(Roles = &lt;span class="str"&gt;"Administrator"&lt;/span&gt;)]
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; GenreController : Controller 
   {
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index() 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
      }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ViewResult Details(&lt;span class="kwrd"&gt;int&lt;/span&gt; id) 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
      }

      &lt;span class="kwrd"&gt;public&lt;/span&gt; ViewResult Create() 
      {
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();
      }
   }
}&lt;/pre&gt;
The routes created for these controllers are:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"store-manager/artist/details/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Artist"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Details"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers.StoreManager"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"store-manager/genre/details/{id}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Genre"&lt;/span&gt;, action = &lt;span class="str"&gt;@"Details"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = &lt;span class="str"&gt;@"0|-?[1-9]\d*"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers.StoreManager"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"store-manager/artist/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Artist"&lt;/span&gt;, action = &lt;span class="str"&gt;@"index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"index|create"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers.StoreManager"&lt;/span&gt; });

routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"store-manager/genre/{action}"&lt;/span&gt;, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;@"Genre"&lt;/span&gt;, action = &lt;span class="str"&gt;@"index"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt; { action = &lt;span class="str"&gt;@"index|create"&lt;/span&gt; }, 
    &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"MvcMusicStore.Controllers.StoreManager"&lt;/span&gt; });&lt;/pre&gt;
Unlike areas, &lt;b&gt;there's no limit to the depth of namespaces&lt;/b&gt; you can use with MvcCodeRouting. There's also support for &lt;b&gt;namespace-aware views location&lt;/b&gt;. For example, views for the &lt;code&gt;MvcMusicStore.Controllers.StoreManager.Artist&lt;/code&gt; controller can be located in &lt;code&gt;~/Views/StoreManager/Artist/&lt;/code&gt;.

&lt;br /&gt;
&lt;h2&gt;
Conclusions&lt;/h2&gt;
&lt;div&gt;
&lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt;&amp;nbsp;is an attempt to make you almost completely &lt;b&gt;forget about route management and routing issues&lt;/b&gt;, a routing-based URL to code mapping solution. Give it a try!&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/9159506019263254704/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/09/5-reasons-why-you-should-use-mvccoderouting.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/9159506019263254704?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/9159506019263254704?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/09/5-reasons-why-you-should-use-mvccoderouting.html" title="5 reasons why you should use MvcCodeRouting" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>8</thr:total></entry><entry gd:etag="W/&quot;DEMGQXY8fCp7ImA9WhNRGEg.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-1581623014815150054</id><published>2012-08-17T21:51:00.000-04:00</published><updated>2012-11-13T22:27:00.874-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T22:27:00.874-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net web api" /><title>What's really new in ASP.NET MVC 4 (not much)</title><content type="html">&lt;h2&gt;
Attributes&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=&lt;span class="kwrd"&gt;false&lt;/span&gt;, Inherited=&lt;span class="kwrd"&gt;true&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;sealed&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;b&gt;AllowAnonymousAttribute&lt;/b&gt; : Attribute
&lt;/pre&gt;
This new attribute allows you to secure an entire controller using &lt;code&gt;AuthorizeAttribute&lt;/code&gt;&amp;nbsp;but still expose individual actions for anonymous access.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;
&lt;pre class="csharpcode"&gt;[AttributeUsage(AttributeTargets.Method, AllowMultiple=&lt;span class="kwrd"&gt;false&lt;/span&gt;, Inherited=&lt;span class="kwrd"&gt;true&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;sealed&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;b&gt;HttpHeadAttribute&lt;/b&gt; : ActionMethodSelectorAttribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple=&lt;span class="kwrd"&gt;false&lt;/span&gt;, Inherited=&lt;span class="kwrd"&gt;true&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;sealed&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;b&gt;HttpOptionsAttribute&lt;/b&gt; : ActionMethodSelectorAttribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple=&lt;span class="kwrd"&gt;false&lt;/span&gt;, Inherited=&lt;span class="kwrd"&gt;true&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;sealed&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;b&gt;HttpPatchAttribute&lt;/b&gt; : ActionMethodSelectorAttribute&lt;/pre&gt;
These new attributes provide the same functionality as &lt;code&gt;HttpPost&lt;/code&gt;, &lt;code&gt;HttpGet&lt;/code&gt;, &lt;code&gt;HttpPut&lt;/code&gt; and &lt;code&gt;HttpDelete&lt;/code&gt;, but for HEAD, OPTIONS and PATCH HTTP methods. &lt;code&gt;HttpHead&lt;/code&gt; is in my view useless, since the whole point of the HEAD method is the check if a GET request would succeed, therefore the same action that handles GET should handle HEAD. An &lt;code&gt;HttpGetHead&lt;/code&gt; attribute would have been more useful, fortunately it's easy to implement.&lt;br /&gt;
&lt;h2&gt;
New HtmlHelper extensions

&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;Html.DisplayName(&lt;span class="kwrd"&gt;string&lt;/span&gt; expression);
Html.DisplayNameFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression);&lt;/pre&gt;
These extensions are used to get the display name of a type or property, before you had to get an instance of the ModelMetadata class for the type or property, now it's conveniently available from HtmlHelper.


&lt;br /&gt;
&lt;pre class="csharpcode"&gt;Html.TextBox(&lt;span class="kwrd"&gt;string&lt;/span&gt; name, &lt;span class="kwrd"&gt;object&lt;/span&gt; value, &lt;span class="kwrd"&gt;string&lt;/span&gt; format);
Html.TextBox(&lt;span class="kwrd"&gt;string&lt;/span&gt; name, &lt;span class="kwrd"&gt;object&lt;/span&gt; value, &lt;span class="kwrd"&gt;string&lt;/span&gt; format, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.TextBox(&lt;span class="kwrd"&gt;string&lt;/span&gt; name, &lt;span class="kwrd"&gt;object&lt;/span&gt; value, &lt;span class="kwrd"&gt;string&lt;/span&gt; format, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.TextBoxFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; format);
Html.TextBoxFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; format, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.TextBoxFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; format, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);&lt;/pre&gt;
These new overloads for rendering textboxes include an additional &lt;code&gt;format&lt;/code&gt; parameter, which internally call &lt;code&gt;String.Format&lt;/code&gt; to customize how the value is rendered.

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;Html.Label(&lt;span class="kwrd"&gt;string&lt;/span&gt; expression, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.Label(&lt;span class="kwrd"&gt;string&lt;/span&gt; expression, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.Label(&lt;span class="kwrd"&gt;string&lt;/span&gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.Label(&lt;span class="kwrd"&gt;string&lt;/span&gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.LabelFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.LabelFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.LabelFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; labelText);
Html.LabelFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.LabelFor(Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.LabelForModel(IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.LabelForModel(&lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);
Html.LabelForModel(&lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, IDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; htmlAttributes);
Html.LabelForModel(&lt;span class="kwrd"&gt;string&lt;/span&gt; labelText, &lt;span class="kwrd"&gt;object&lt;/span&gt; htmlAttributes);&lt;/pre&gt;
These new overloads for rendering labels include the standard &lt;code&gt;htmlAttributes&lt;/code&gt; parameter, it was simply an oversight that these were not included in v3.

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;Html.Id(&lt;span class="kwrd"&gt;string&lt;/span&gt; name);
Html.IdFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression);
Html.IdForModel();
Html.Name(&lt;span class="kwrd"&gt;string&lt;/span&gt; name);
Html.NameFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression);
Html.NameForModel();&lt;/pre&gt;
These new extensions render the full id or name from a partial name or expression. This functionality was available in v3, but now it's much easier to call on HtmlHelper.&lt;br /&gt;
&lt;pre class="csharpcode"&gt;Html.Value(&lt;span class="kwrd"&gt;string&lt;/span&gt; name);
Html.Value(&lt;span class="kwrd"&gt;string&lt;/span&gt; name, &lt;span class="kwrd"&gt;string&lt;/span&gt; format);
Html.ValueFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression);
Html.ValueFor(Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; expression, &lt;span class="kwrd"&gt;string&lt;/span&gt; format);
Html.ValueForModel();
Html.ValueForModel(&lt;span class="kwrd"&gt;string&lt;/span&gt; format);&lt;/pre&gt;
These new extensions render the value of a property from model state, view data or view model.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; FormatValue(&lt;span class="kwrd"&gt;object&lt;/span&gt; value, &lt;span class="kwrd"&gt;string&lt;/span&gt; format);
&lt;span class="kwrd"&gt;public&lt;/span&gt; IHtmlString Raw(&lt;span class="kwrd"&gt;object&lt;/span&gt; value);&lt;/pre&gt;
These are not extensions but instance methods of HtmlHelper. &lt;code&gt;FormatValue&lt;/code&gt; simply calls &lt;code&gt;String.Format&lt;/code&gt;, and the new &lt;code&gt;Raw&lt;/code&gt; overload allows you to pass any value without having to convert to string.
&lt;br /&gt;
&lt;h2&gt;
New UrlHelper methods&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; HttpRouteUrl(&lt;span class="kwrd"&gt;string&lt;/span&gt; routeName, &lt;span class="kwrd"&gt;object&lt;/span&gt; routeValues);
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; HttpRouteUrl(&lt;span class="kwrd"&gt;string&lt;/span&gt; routeName, RouteValueDictionary routeValues);&lt;/pre&gt;
These new methods are used to generate URLs for Web Api routes. Apparently, Web Api routes are "special" and cannot be matched using &lt;code&gt;UrlHelper.RouteUrl&lt;/code&gt;. Internally, these methods add an "httproute" entry to &lt;code&gt;routeValues&lt;/code&gt;, and the GetVirtualPath method of the Web Api route classes (there's more than one) return null unless you include "httproute" in the values. What a mess...

&lt;br /&gt;
&lt;h2&gt;
New Controller properties&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; AsyncManager AsyncManager { &lt;span class="kwrd"&gt;get&lt;/span&gt;; }
&lt;span class="kwrd"&gt;public&lt;/span&gt; ProfileBase Profile { &lt;span class="kwrd"&gt;get&lt;/span&gt;; }
&lt;span class="kwrd"&gt;public&lt;/span&gt; ViewEngineCollection ViewEngineCollection { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; Boolean DisableAsyncSupport { &lt;span class="kwrd"&gt;get&lt;/span&gt;; }&lt;/pre&gt;
Profile is just a shortcut to &lt;code&gt;this.HttpContext.Profile&lt;/code&gt;. ViewEngineCollection is just a shortcut to &lt;code&gt;ViewEngines.Engines&lt;/code&gt;. The new async related properties are there because now all controllers are executed asynchronously by default.

&lt;br /&gt;
&lt;h2&gt;
That's about it&lt;/h2&gt;
There are some other new types, some new members, some obsoleted members, some deleted or type-forwarded types, but I don't want to bore you and myself to death. There's nothing new in MVC 4, except for...&lt;br /&gt;
&lt;h2&gt;
The real new features&lt;/h2&gt;
In reality there are only 2 new features, &lt;b&gt;Display Modes&lt;/b&gt; and &lt;b&gt;returning Task from an async action&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
As I &lt;a href="http://maxtoroq.blogspot.com/2012/02/aspnet-mvc-4-beta-web-api-is-not-mvc.html"&gt;pointed out before&lt;/a&gt;, Web Api is not MVC, it doesn't depend on MVC, it doesn't even depend on ASP.NET. Bundling and minification can be used on any ASP.NET project. jQuery UI, Entity Framework, OpenID, all separate libraries. This release feels more like v3.1 than v4.&lt;br /&gt;
&lt;h2&gt;
MVC and Web Api&lt;/h2&gt;
&lt;div&gt;
The reason because MVC and Web Api are released and distributed together is..., well..., I have no idea why. There's no technical reason behind that decision. I guess Microsoft thought people would probably use both on the same project. There's no Web Api project template on Visual Studio, you have to create an MVC project to use Web Api.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
One year from now Microsoft will be eager to release Web Api v2 after recieving massive feedback and bug reports, will there be a new major release for MVC? I'm not sure, maybe they'll release a new major version, but with only a couple of new features just like this one. MVC is now on maintenence mode, just like Web Forms and LINQ to SQL. MVC 3 was already an excelent, mature and extensible framework. It's not up to Microsoft, but it's up to the community to build more cool stuff on top of it.&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Update&lt;/b&gt;: The ASP.NET team have confirmed my suspicion in their &lt;a href="http://aspnetwebstack.codeplex.com/wikipage?title=Roadmap&amp;amp;version=35"&gt;roadmap&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote class="tr_bq"&gt;
No new functionality will be added to the runtime components. Instead the area of focus will be enabling a richer set of templates for building various types of web applications developers need in addition to updating the current templates.&lt;/blockquote&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/1581623014815150054/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/08/whats-really-new-in-aspnet-mvc-4-not-much.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1581623014815150054?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1581623014815150054?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/08/whats-really-new-in-aspnet-mvc-4-not-much.html" title="What's really new in ASP.NET MVC 4 (not much)" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DEMBQ3Y5eSp7ImA9WhNRGEg.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-8823047252947102296</id><published>2012-08-11T18:17:00.000-04:00</published><updated>2012-11-13T22:27:32.821-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T22:27:32.821-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><category scheme="http://www.blogger.com/atom/ns#" term="html" /><title>Implementing a Delete button in ASP.NET MVC</title><content type="html">I've been reading the HTML 4 and 5 specs lately, more specifically about forms, and learned something new. I knew a button could have a name and value, what I didn't know is that a button is considered a successful control, which means its data is sent to the server, only if the form submission is triggered by that button. This is a cool feature that allows your server logic to know which button was clicked and perform different operations based on that. A very common scenario is implementing a delete button.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
In the past I've done this in a number of ways: AJAX, a separate form, etc. Usually, people use a different POST action for delete, e.g.&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Edit(&lt;span class="kwrd"&gt;int&lt;/span&gt; id) {
   ...
}

[HttpPost]
&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Delete(&lt;span class="kwrd"&gt;int&lt;/span&gt; id) {
   ...
}&lt;/pre&gt;
Taking advantage of the framework's support for X-HTTP-Method-Override, you can implement a delete button, on the same form, that POSTs to the same URL...&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Edit(&lt;span class="kwrd"&gt;int&lt;/span&gt; id) {
   ...
}

[HttpDelete]
&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Edit(&lt;span class="kwrd"&gt;int&lt;/span&gt; id, &lt;span class="kwrd"&gt;string&lt;/span&gt; foo) {
   ...
}&lt;/pre&gt;
...like this:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@&lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; (Html.BeginForm()) {
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="X-HTTP-Method-Override"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="DELETE"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
}&lt;/pre&gt;
Easy, right? X-HTTP-Method-Override is sent only when you click the delete button.&amp;nbsp;You can also include &lt;code&gt;onclick="return confirm('Are you sure?')"&lt;/code&gt; for a confirmation dialog.&lt;br /&gt;
&lt;br /&gt;
There's one catch though, validation. If the form is not valid it won't submit. This is rarely an issue, but it can happen if the user made some changes that leaves the form in an invalid state and then decides to delete the resource. HTML5 supports this scenario using the &lt;code&gt;formnovalidate="formnovalidate"&lt;/code&gt; attribute on the button. The jQuery validation plugin doesn't seem to recognize this attribute, but you can achieve the same behavior using the &lt;code&gt;cancel&lt;/code&gt; class on the button. So, this is the final version of our 100% declarative delete button:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="X-HTTP-Method-Override"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="DELETE"&lt;/span&gt; &lt;span class="attr"&gt;formnovalidate&lt;/span&gt;&lt;span class="kwrd"&gt;="formnovalidate"&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="cancel"&lt;/span&gt; 
   &lt;span class="attr"&gt;onclick&lt;/span&gt;&lt;span class="kwrd"&gt;="return confirm('Are you sure?')"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;button&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/8823047252947102296/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/08/implementing-delete-button-in-aspnet-mvc.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8823047252947102296?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8823047252947102296?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/08/implementing-delete-button-in-aspnet-mvc.html" title="Implementing a Delete button in ASP.NET MVC" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DU8NQ3s7fCp7ImA9WhBTF0o.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-6255578495867448318</id><published>2012-07-24T00:01:00.000-04:00</published><updated>2013-02-13T14:51:32.504-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-13T14:51:32.504-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="patterns" /><category scheme="http://www.blogger.com/atom/ns#" term="plugin" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Patterns for ASP.NET MVC Plugins: View Models</title><content type="html">The goal of these patterns is to provide a consistent experience for plugin consumers (application developers), minimize the amount of configuration required to get a plugin working in the host application and maximize the plugin's flexibility to customize it's behavior.&lt;br /&gt;
&lt;br /&gt;
This is the second post of the series, see also the other posts:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html"&gt;Routes, Controllers and Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;View Models&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/10/implementing-contact-form-plugin-for-aspnet-mvc.html"&gt;Demo: Implementing a contact form plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
Basic patterns&lt;/h2&gt;
&lt;b id="pattern-viewmodels-use"&gt;Always use strongly-typed view models&lt;/b&gt;. Don't use ViewData, ViewBag or a dynamic/anonymous object, always create a specialized view model class for each view. Application developers will appreciate it when customizing views.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-naming"&gt;Name view models after the view plus the "ViewModel" suffix&lt;/b&gt;. e.g. for the Index view create an IndexViewModel class. Although ViewModel is a long suffix, it makes it very clear for the developer to understand what's the purpose of the class, and to differentiate from other kinds of models the plugin might have. If you have more than one controller use the controller name as prefix, e.g. FooIndexViewModel.&lt;br /&gt;
&lt;br /&gt;
&lt;strike&gt;&lt;b&gt;Customize display names using DisplayNameAttribute instead of DisplayAttribute&lt;/b&gt;. There are two ways of customizing the display names of types and properties. You can use the Name property of the &lt;a href="http://msdn.microsoft.com/library/system.componentmodel.dataannotations.displayattribute"&gt;DisplayAttribute&lt;/a&gt; or you can use the &lt;a href="http://msdn.microsoft.com/library/system.componentmodel.displaynameattribute"&gt;DisplayNameAttribute&lt;/a&gt;, the framework recognizes both in that order. You should use DisplayNameAttribute because it's specifically design for that purpose (DisplayAttribute has many other properties), and more importantly because DisplayAttribute is sealed and DisplayNameAttribute isn't. If some component needs to customize how display names are computed subclassing DisplayNameAttribute is the only way, a common example is reading display names from resource files for localization. You can still use DisplayAttribute for all the other properties it has, just don't use Name.&lt;/strike&gt;&amp;nbsp;&lt;span style="background-color: white;"&gt;&lt;b&gt;Update:&lt;/b&gt;&lt;span style="color: red;"&gt;&amp;nbsp;&lt;/span&gt;I was under the impression that DisplayAttribute did not support localization, but I was wrong. DisplayAttribute fixes two issues about DisplayNameAttribute: 1) It supports localization, 2) It can be applied to fields (e.g. enums members). So, if your plugin targets .NET 4 or higher you should use DisplayAttribute. If you care about maximum compatibility, e.g. with libraries that target .NET 2-3.5, or that maybe recognize only one of them, you should use both.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-display-order"&gt;Set Display(Order) for all properties&lt;/b&gt;. When you subclass a model to add more properties, these added properties will appear at the top of the form, before the properties from the base class. Normally you want fields in a form to be sorted by importance, so in most cases it would make more sense to see the subclass properties at the bottom. Since that is not the case you should explicitly set Display(Order) on all properties. Note that having more than one property with the same Order value is allowed, in that case the subclass property also goes first.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-data-type-attr"&gt;Use the DataTypeAttribute when appropiate&lt;/b&gt;. See if your properties are used for values that can be represented by one of the members in the &lt;a href="http://msdn.microsoft.com/library/system.componentmodel.dataannotations.datatype"&gt;DataType&lt;/a&gt; enumeration. Common cases are Password, MultilineText and EmailAddress. Setting this attribute makes it easier to customize display and editor templates for each of these types of values.&lt;br /&gt;
&lt;h2&gt;
View Models, Input Models and Output Models&lt;/h2&gt;
I want to make a distinction between the different kinds of models, based on their purpose. A &lt;b&gt;view model&lt;/b&gt; is a model for the view, it defines the data that the view needs and uses, nothing less nothing more. An &lt;b&gt;input model&lt;/b&gt; defines the data that an action takes as input, e.g. from a form submission. An &lt;b&gt;output model&lt;/b&gt;&amp;nbsp;defines the data that is the result of an action, e.g. search results. Some actions have inputs but no outputs (e.g. actions that redirect), some actions have outputs but no inputs, and some have both inputs and outputs.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-separate-classes"&gt;Define separate classes for input and output models, plus a view model class that reference the other two&lt;/b&gt;.&amp;nbsp;Instead of defining a single view model class that contain both input and output properties define a class for each of these types of models, plus a view model class with an &lt;b&gt;InputModel&lt;/b&gt; property (for actions that have inputs) and an &lt;b&gt;OutputModel&lt;/b&gt; property (for actions that have outputs).&lt;br /&gt;
&lt;br /&gt;
Although the framework has a BindAttribute and several [Try]UpdateModel overloads where you can specify which input properties should be bound, or which output properties should not be bound, using it means that every time you want to extend an input or output model you would have to update the action code, which is not an option for precompiled plugins. The following sections talk about input and output model extensibility.&lt;br /&gt;
&lt;br /&gt;
Another reason for having separate input and output models is editor and display templates. Application developers should be able to customize editor and display templates for specific types without having to override an entire view to do so.&lt;br /&gt;
&lt;h2&gt;
Input Models&lt;/h2&gt;
&lt;b id="pattern-viewmodels-inputmodel-naming"&gt;Name input models after the action method name plus the "Input" suffix&lt;/b&gt;. e.g. for the ChangeEmail action create the ChangeEmailInput input model. Notice I said action method name instead of action name, sometimes action names are shortened (e.g. using the ActionNameAttribute) or changed in some way to make the URL suit a particular style. The name of input models should be meaningful to application developers and don't need to match the action URL. If you have more than one controller use the controller name as prefix, e.g. AccountChangeEmailInput.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-inputmodel-naming-edit"&gt;If you have &lt;i&gt;Create&lt;/i&gt; and &lt;i&gt;Update&lt;/i&gt; actions that have identical input models define a single input model named after the resource plus the "Edit" suffix&lt;/b&gt;. e.g. ProductEdit. This one is for actions that are more "resource oriented". If one of the models contains a subset of the other consider making one inherit the other to avoid having duplicate properties (defining identical properties is not the main issue, is more about all the metadata you put on them via attributes).&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-inputmodel-no-id"&gt;Don't include unique identifier properties&lt;/b&gt;. Input models have no identity, if you are using an input model to provide changes to some existing resource pass in the resource identifier as a separate argument. For example, let's say we have an action to edit a single product:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;[HttpPost]
&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Edit(&lt;span class="kwrd"&gt;int&lt;/span&gt; id, ProductEdit input) {
   ...
}&lt;/pre&gt;
The id parameter identifies the product, and the ProductEdit input model defines the properties that can be changed:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ProductEdit {
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Name { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;decimal&lt;/span&gt; UnitPrice { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
}&lt;/pre&gt;
If you want to display the id on the view you should include it in an output model.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-htmlhelper-for-inputmodel"&gt;Create an HtmlHelper&amp;lt;TModel&amp;gt; instance for form rendering, where TModel is the input model type&lt;/b&gt;. It's common to use an input model as view model to provide strongly-typed HTML helpers for form controls and validation messages, e.g. &lt;code&gt;Html.EditorFor(p =&amp;gt; p.SomeProperty)&lt;/code&gt;. By having a separate input model type that the view model includes in a property, strongly-typed HTML helper calls are changed to something like &lt;code&gt;Html.EditorFor(p =&amp;gt; p.InputModel.SomeProperty)&lt;/code&gt;, which means all form fields will have the "InputModel." prefix. You can avoid this by creating an HtmlHelper&amp;lt;TModel&amp;gt; instance, where TModel is the input model type. The following is an extension method for this purpose:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; HtmlHelperFactoryExtensions {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; HtmlHelper&amp;lt;TModel&amp;gt; HtmlHelperFor&amp;lt;TModel&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; HtmlHelper htmlHelper) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; HtmlHelperFor(htmlHelper, &lt;span class="kwrd"&gt;default&lt;/span&gt;(TModel));
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; HtmlHelper&amp;lt;TModel&amp;gt; HtmlHelperFor&amp;lt;TModel&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; HtmlHelper htmlHelper, TModel model) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; HtmlHelperFor(htmlHelper, model, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; HtmlHelper&amp;lt;TModel&amp;gt; HtmlHelperFor&amp;lt;TModel&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; HtmlHelper htmlHelper, TModel model, &lt;span class="kwrd"&gt;string&lt;/span&gt; htmlFieldPrefix) {

      &lt;span class="kwrd"&gt;var&lt;/span&gt; viewDataContainer = CreateViewDataContainer(htmlHelper.ViewData, model);

      TemplateInfo templateInfo = viewDataContainer.ViewData.TemplateInfo;

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (!String.IsNullOrEmpty(htmlFieldPrefix))
         templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(htmlFieldPrefix);

      ViewContext viewContext = htmlHelper.ViewContext;
      ViewContext newViewContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; ViewContext(viewContext.Controller.ControllerContext, viewContext.View, viewDataContainer.ViewData, viewContext.TempData, viewContext.Writer);

      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; HtmlHelper&amp;lt;TModel&amp;gt;(newViewContext, viewDataContainer, htmlHelper.RouteCollection);
   }

   &lt;span class="kwrd"&gt;static&lt;/span&gt; IViewDataContainer CreateViewDataContainer(ViewDataDictionary viewData, &lt;span class="kwrd"&gt;object&lt;/span&gt; model) {

      &lt;span class="kwrd"&gt;var&lt;/span&gt; newViewData = &lt;span class="kwrd"&gt;new&lt;/span&gt; ViewDataDictionary(viewData) {
         Model = model
      };

      newViewData.TemplateInfo = &lt;span class="kwrd"&gt;new&lt;/span&gt; TemplateInfo { 
         HtmlFieldPrefix = newViewData.TemplateInfo.HtmlFieldPrefix 
      };

      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ViewDataContainer {
         ViewData = newViewData
      };
   }

   &lt;span class="kwrd"&gt;class&lt;/span&gt; ViewDataContainer : IViewDataContainer {
         
      &lt;span class="kwrd"&gt;public&lt;/span&gt; ViewDataDictionary ViewData { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   }
}&lt;/pre&gt;
This is how you use it in a view:
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;@&lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; (Html.BeginForm()) {
   &lt;span class="kwrd"&gt;var&lt;/span&gt; inputHtml = Html.HtmlHelperFor(Model.InputModel);
   &lt;span class="asp"&gt;@&lt;/span&gt;inputHtml.EditorForModel()
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="submit"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
}&lt;/pre&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-selectlist-viewdata"&gt;Use ViewData to pass a [Multi]SelectList instance for multiple choice controls&lt;/b&gt;. This is one case where using ViewData has benefits. It provides an out-of-band way of making a [Multi]SelectList available to multiple choice HTML helpers such as DropDownList, which means the view that calls the helper does not need to provide it as argument or can just pass null. To avoid losing type safety you can use the following method:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; SetSelectList&amp;lt;TModel, TProperty&amp;gt;(TModel model, Expression&amp;lt;Func&amp;lt;TModel, TProperty&amp;gt;&amp;gt; propertySelector, MultiSelectList list) {
   &lt;span class="kwrd"&gt;this&lt;/span&gt;.ViewData[ExpressionHelper.GetExpressionText(propertySelector)] = list;
}&lt;/pre&gt;
This is how you use it:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;SetSelectList(model, m =&amp;gt; m.Country, selectList);&lt;/pre&gt;
&lt;div&gt;
&lt;b id="pattern-viewmodels-inputmodel-create"&gt;Provide a virtual method for the creation of input models, and name it after the type name plus the "Create" prefix&lt;/b&gt;. e.g.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; ChangeEmailInput CreateChangeEmailInput() {
   &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ChangeEmailInput();
}&lt;/pre&gt;
This method should be defined in the controller dependency that uses the input model. By overriding this method you can return a subclass of the input model.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-inputmodel-initialize"&gt;Provide a virtual method for the initialization of input models, and name it after the type name plus the "Initialize" prefix&lt;/b&gt;. e.g.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; InitializeChangeEmailInput(ChangeEmailInput input) { }&lt;/pre&gt;
This method should be defined in the controller dependency that uses the input model.&amp;nbsp;This method is for providing default or initial values to form fields. You can also define additional parameters, e.g. if you want to initialize with persisted values. If the input model is used in more than one action append "For" to the method name plus the name of the method that uses the input model, e.g. InitializeProductEditForCreate, InitializeProductEditForUpdate.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-inputmodel-virtual-properties"&gt;Use the virtual modifier on all input model properties&lt;/b&gt;. This allows the subclass to add or override attributes.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-modelbinding-inheritance"&gt;Use a model binding implementation that supports subclassing&lt;/b&gt;. Unfortunately, the [Try]UpdateModel methods provided by the framework require that you explicitly declare the type you are binding at compile time, and if you attempt to bind a subclass instance the subclass properties are ignored. Even though they closed &lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/483001"&gt;the issue&lt;/a&gt;&amp;nbsp;claiming it's by design, let them know it's a bad design. Use the following code to workaround this issue:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ModelBinderUtil {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; TryUpdateModel(&lt;span class="kwrd"&gt;object&lt;/span&gt; model, Controller controller) {

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (model == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class="str"&gt;"model"&lt;/span&gt;);
      &lt;span class="kwrd"&gt;if&lt;/span&gt; (controller == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class="str"&gt;"controller"&lt;/span&gt;);

      Type modelType = model.GetType();

      ModelBinders.Binders
         .GetBinder(modelType)
         .BindModel(controller.ControllerContext, &lt;span class="kwrd"&gt;new&lt;/span&gt; ModelBindingContext {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() =&amp;gt; model, modelType),
            ModelState = controller.ModelState,
            ValueProvider = controller.ValueProvider
         });

      &lt;span class="kwrd"&gt;return&lt;/span&gt; controller.ModelState.IsValid;
   }
}&lt;/pre&gt;
&lt;b id="pattern-viewmodels-modelbinding-failure-status-code"&gt;Set Response.StatusCode to 400 (Bad Request) if model binding fails&lt;/b&gt;. Although users don't know or care about the status codes, it's important to set an error status code for AJAX clients. Even if your plugin does not implement AJAX forms, the plugin consumer might want to enhance the plugin with AJAX submissions, and the only (appropriate) way to know if the request succeeded or not is by examining the response status code.&lt;br /&gt;
&lt;h2&gt;
Output Models&lt;/h2&gt;
&lt;b id="pattern-viewmodels-outputmodel-naming"&gt;Name output models after the action method name plus the "Result" suffix&lt;/b&gt;. e.g. SearchResult. If you have more than one controller use the controller name as prefix, e.g. ProductsSearchResult.
&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-output-on-viewmodel"&gt;Consider using view model properties for metadata and presentation data&lt;/b&gt;. It's usually cleaner to pass things like links, titles, messages or other unrelated pieces of data that the view needs, as properties of the view model. This might be data that complement the output model, but created in a different layer of the plugin (e.g. the "Cancel" link of a form). It can also be presentation-specific data, such as the active tab in a tabbed interface. In other words, output models should be used for "business" or "domain" data, and view models for application or presentation data. It's up to the plugin developer to decide where to draw the line, but should always prioritize usability from the application developer's perspective.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-outputmodel-create"&gt;Provide a virtual method for the creation of output models, and name it after the type name plus the "Create" prefix&lt;/b&gt;. e.g.&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; SearchResult CreateSearchResult() {
   &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SearchResult();
}&lt;/pre&gt;
This method should be defined in the controller dependency that uses the output model. By overriding this method you can return a subclass of the output model.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-viewmodels-outputmodel-bind"&gt;Provide a virtual method for the binding of output models, and name it after the type name plus the "Bind" prefix&lt;/b&gt;. e.g.
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; BindSearchResult(SearchResult result, &lt;span class="kwrd"&gt;object&lt;/span&gt; data) { }&lt;/pre&gt;
This method should be defined in the controller dependency that uses the output model. The type of the second parameter is implementation-defined.&lt;br /&gt;
&lt;h2&gt;
Conclusions&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Good view model design can make a great difference in the plugin's customizability. We've covered ways to customize the data that comes in and out of the plugin, their presentation and validation metadata. Naming patterns are also very important for usability, it makes it easier for application developers to find and discover the types they need to use.&lt;/div&gt;
&lt;h2&gt;
Tying it all together&lt;/h2&gt;
&lt;div&gt;
On the next post I'm going to dissect a sample plugin that implements most of the patterns presented on this series so far. Stay tuned.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/6255578495867448318/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/6255578495867448318?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/6255578495867448318?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html" title="Patterns for ASP.NET MVC Plugins: View Models" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DU4FRXszeSp7ImA9WhBTF0o.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-3901494913049214461</id><published>2012-06-11T00:43:00.000-04:00</published><updated>2013-02-13T14:51:54.581-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-13T14:51:54.581-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="patterns" /><category scheme="http://www.blogger.com/atom/ns#" term="plugin" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Patterns for ASP.NET MVC Plugins: Routes, Controllers and Configuration</title><content type="html">&lt;div&gt;
The goal of these patterns is to provide a consistent experience for plugin consumers (application developers), minimize the amount of configuration required to get a plugin working in the host application and maximize the plugin's flexibility to customize it's behavior.&lt;br /&gt;
&lt;br /&gt;
This post focuses on routes, controllers and configuration. Subsequent posts will focus on other aspects, such as views, models, localization, etc.&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;i&gt;Routes, Controllers and Configuration&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html"&gt;View Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://maxtoroq.blogspot.com/2012/10/implementing-contact-form-plugin-for-aspnet-mvc.html"&gt;Demo: Implementing a contact form plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
Routes&lt;/h2&gt;
&lt;div&gt;
&lt;b id="pattern-routes-dont-require-custom"&gt;Don't require any custom route configuration&lt;/b&gt;, make it an option for prettier URLs. In practice this means the plugin shouldn't depend or assume a specific route configuration:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Don't refer to named routes&lt;/b&gt;&amp;nbsp;e.g. &lt;code&gt;Url.RouteUrl("routeName")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Don't hardcode URLs&lt;/b&gt; e.g. &lt;code&gt;return Redirect("/Foo/Bar")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;b id="pattern-routes-dont-add-to-global-table"&gt;Don't add routes to RouteTable.Routes.&lt;/b&gt;&amp;nbsp;The application developer should decide how to expose the plugin, if at all.&amp;nbsp;The plugin should work fine with the default route &lt;code&gt;{controller}/{action}/{id}&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-routes-provide-custom-routes-sample-code"&gt;Provide sample code of custom routes&lt;/b&gt; the application developer can copy and paste into the host application for prettier URLs.&lt;/div&gt;
&lt;h2&gt;
Controllers&lt;/h2&gt;
&lt;b id="pattern-controllers-public"&gt;Make controllers public&lt;/b&gt;&amp;nbsp;so they can be found by the DefaultControllerFactory.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-controllers-common-naming"&gt;Name controllers with common, widely used names&lt;/b&gt;. If the plugin is about authentication, AccountController; A blog, BlogController; A forum, ForumController. This makes the plugin default route friendly.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-controllers-id-param"&gt;Use a parameter named 'id' for unique identifier inputs&lt;/b&gt;. Again, make the inputs default route compatible.&lt;br /&gt;
&lt;br /&gt;
&lt;b id="pattern-controllers-minimize"&gt;Minimize the number of controllers&lt;/b&gt;. Ideally, the plugin should consist of a single controller. If more than one controller is used, put them all in the same namespace.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-controllers-default-constructor"&gt;Provide a default constructor&lt;/b&gt;&amp;nbsp;so they can be instantiated by DefaultControllerFactory. Beginners are usually not familiar nor comfortable with&amp;nbsp;container assisted&amp;nbsp;dependency injection (DI). The next section explains an alternate way of providing dependencies that doesn't require a DI container.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;b id="pattern-controllers-constructor-dependencies"&gt;Provide constructors that take dependencies as arguments&lt;/b&gt;&amp;nbsp;for applications already using a DI container.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There are two kinds of dependencies: &lt;b&gt;abstract &lt;/b&gt;and &lt;b&gt;concrete&lt;/b&gt;. These can also be viewed as &lt;b&gt;required &lt;/b&gt;and &lt;b&gt;optional&lt;/b&gt;. Abstract dependencies represent functionality that the application developer is &lt;b&gt;required&lt;/b&gt;&amp;nbsp;to implement before they can use the plugin. These take the form of abstract types or interfaces. Concrete dependencies represent functionality already provided by the plugin, but that the application developer has the &lt;b&gt;option&lt;/b&gt;&amp;nbsp;to customize. These take the form of non-abstract, non-static classes with virtual public and/or protected members.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-controllers-constructor-dependencies-all"&gt;Provide a constructor that take all abstract and concrete dependencies. &lt;/b&gt;Put abstract dependencies first, then concrete dependencies. Many DI containers use by default the constructor with most parameters. By having a constructor that take all dependencies, the application developer will have the option to subclass and customize any/all concrete dependencies with minimum configuration. &lt;b&gt;Do not provide another constructor with the same number of parameters.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-controllers-constructor-dependencies-all-abstract-none-concrete"&gt;Provide a constructor that take all abstract dependencies and none of the concrete dependencies.&lt;/b&gt;&amp;nbsp;Some DI containers (like &lt;a href="http://code.google.com/p/autofac/"&gt;Autofac&lt;/a&gt;) use by default the constructor with the most parameters &lt;b&gt;they know how to resolve&lt;/b&gt;. Don't force developers to register concrete dependencies, make that an option.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-controllers-constructor-dependencies-all-abstract-some-concrete"&gt;Provide other constructors that&amp;nbsp;take all abstract dependencies and some of the concrete dependencies.&lt;/b&gt;&amp;nbsp;Don't force developers to register all concrete dependencies, if they only want to customize a subset of them.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
The following is an example that follow these recommendations, &lt;code&gt;AccountRepository&lt;/code&gt; and &lt;code&gt;PasswordService&lt;/code&gt; are abstract (required) dependencies, and&amp;nbsp;&lt;code&gt;FormsAuthenticationService&lt;/code&gt; is concrete (optional).&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;AccountController&lt;/span&gt; : &lt;span class="type"&gt;Controller&lt;/span&gt; {

   &lt;span class="kwrd"&gt;readonly&lt;/span&gt; &lt;span class="type"&gt;AccountService&lt;/span&gt; service;

   &lt;span class="kwrd"&gt;public&lt;/span&gt; AccountController() 
      : &lt;span class="kwrd"&gt;this&lt;/span&gt;(&lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;AccountService&lt;/span&gt;()) { }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; AccountController(&lt;span class="type"&gt;AccountRepository&lt;/span&gt; repository, &lt;span class="type"&gt;PasswordService&lt;/span&gt; passwordService)
      : &lt;span class="kwrd"&gt;this&lt;/span&gt;(&lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;AccountService&lt;/span&gt;(repository, passwordService)) { }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; AccountController(&lt;span class="type"&gt;AccountRepository&lt;/span&gt; repository, &lt;span class="type"&gt;PasswordService&lt;/span&gt; passwordService, &lt;span class="type"&gt;FormsAuthenticationService&lt;/span&gt; formsAuthService) 
      : &lt;span class="kwrd"&gt;this&lt;/span&gt;(&lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;AccountService&lt;/span&gt;(repository, passwordService, formsAuthService)) { }

   &lt;span class="kwrd"&gt;private&lt;/span&gt; AccountController(&lt;span class="type"&gt;AccountService&lt;/span&gt; service) {
      &lt;span class="kwrd"&gt;this&lt;/span&gt;.service = service;
   }
}&lt;/pre&gt;
&lt;h2&gt;
Configuration&lt;/h2&gt;
&lt;div&gt;
&lt;b id="pattern-configuration-class"&gt;Provide a class for the plugin's configuration settings&lt;/b&gt;. Make it an instance class with instance properties, not static, not singleton.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-configuration-data-token"&gt;Use the 'Configuration' data token to accept an instance of the configuration class&lt;/b&gt;. Here's an example:&lt;/div&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;BlogController&lt;/span&gt; : &lt;span class="type"&gt;Controller&lt;/span&gt; {
   
   &lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; config;

   &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Initialize(&lt;span class="type"&gt;RequestContext&lt;/span&gt; requestContext) {
      
      &lt;span class="kwrd"&gt;base&lt;/span&gt;.Initialize(requestContext);

      &lt;span class="kwrd"&gt;this&lt;/span&gt;.config = requestContext.RouteData.DataTokens[&lt;span class="str"&gt;"Configuration"&lt;/span&gt;] &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; 
         ?? &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;BlogConfiguration&lt;/span&gt;();
   }
}&lt;/pre&gt;
&lt;div&gt;
This is what the route registration could look like:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoutes(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Blog/{action}/{id}"&lt;/span&gt;
   { controller = &lt;span class="str"&gt;"Blog"&lt;/span&gt;, action = &lt;span class="str"&gt;"Index"&lt;/span&gt;, id = &lt;span class="type"&gt;UrlParameter&lt;/span&gt;.Optional },
   &lt;span class="kwrd"&gt;new&lt;/span&gt;[] { &lt;span class="str"&gt;"CoolBlog.Controllers"&lt;/span&gt; })
   .DataTokens[&lt;span class="str"&gt;"Configuration"&lt;/span&gt;] = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; {
      &lt;span class="rem"&gt;// Set config properties&lt;/span&gt;
   };&lt;/pre&gt;
&lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt; also follows this pattern, with the Configuration setting:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;route.MapCodeRoutes(
   baseRoute: &lt;span class="str"&gt;"Blog"&lt;/span&gt;,
   rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(CoolBlog.Controllers.&lt;span class="type"&gt;BlogController&lt;/span&gt;),
   settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;CodeRoutingSettings&lt;/span&gt; {
      Configuration = &lt;span class="kwrd"&gt;new&lt;/span&gt; CoolBlog.&lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; {
         &lt;span class="rem"&gt;// Set config properties&lt;/span&gt;
      }
   }
);&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;b id="pattern-configuration-resolver-properties"&gt;Provide Func&amp;lt;&amp;gt; properties for each dependency, &amp;nbsp;and name them with the "Resolver" suffix&lt;/b&gt;. Here's an example:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;var&lt;/span&gt; config = &lt;span class="kwrd"&gt;new&lt;/span&gt; CoolBlog.&lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; {
   RepositoryResolver = () =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;MyBlogRepository&lt;/span&gt;()
};&lt;/pre&gt;
A DI container can also be used to resolve dependencies, if object composition or lifetime management is required:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;var&lt;/span&gt; config = &lt;span class="kwrd"&gt;new&lt;/span&gt; CoolBlog.&lt;span class="type"&gt;BlogConfiguration&lt;/span&gt; {
   RepositoryResolver = () =&amp;gt; container.Resolve&amp;lt;CoolBlog.&lt;span class="type"&gt;BlogRepository&lt;/span&gt;&amp;gt;()
};&lt;/pre&gt;
The advantage of associating the plugin's configuration instance, that include resolver properties for DI, with the plugin's route is that you can use more than one plugin instance per application, each with different configuration settings.&lt;br /&gt;
&lt;h2&gt;
Conclusions&lt;/h2&gt;
This post deals with what I think are the most important aspects of plugin development for ASP.NET MVC: &amp;nbsp;routes, controllers and configuration. The MVC framework itself has a number of conventions that application developers are accustomed to; plugin developers should adhere to these conventions. This post proposes patterns that do not require a special infrastructure (e.g. common library for plugins) to work. Instead, it uses what the framework already provides. Plugins are just controllers exposed via routes, just like anything else in the host application. Hopefully these patterns will help you build plugins that are portable, flexible and easy to configure.&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/3901494913049214461/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/3901494913049214461?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/3901494913049214461?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/06/patterns-for-aspnet-mvc-plugins-routes.html" title="Patterns for ASP.NET MVC Plugins: Routes, Controllers and Configuration" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DEQCQ3s-eip7ImA9WhNRGEg.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-1714333762001795334</id><published>2012-04-11T16:33:00.000-03:00</published><updated>2012-11-13T22:26:02.552-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T22:26:02.552-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="routing" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Putting ASP.NET Routing to test</title><content type="html">This post is not about unit-testing your routes, it's about testing the features of ASP.NET Routing to verify and acquire an in-depth knowledge of it's functionality.&lt;br /&gt;
&lt;br /&gt;
Having used ASP.NET MVC for a long time I thought I had a complete understanding about how Routing worked, but the other day I wasn’t sure about something that thought should work and took for granted. So I decided to write some tests to clear my doubts and hopefully learn something new.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Some of these tests verify things you probably already know, if something is too obvious you can just skip to the next one. You don't need to follow the test code, but after each test I've included information on how to take advantage or how to avoid common issues&amp;nbsp;of the tested functionality.&lt;br /&gt;
&lt;h2&gt;
Test class setup&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;RouteCollection routes;
UrlHelper Url;

[TestInitialize]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Init() {
         
   &lt;span class="kwrd"&gt;this&lt;/span&gt;.routes = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteCollection();

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.ApplicationPath).Returns(&lt;span class="str"&gt;""&lt;/span&gt;);
   httpContextMock.Setup(c =&amp;gt; c.Response.ApplyAppPathModifier(It.IsAny&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;()))
      .Returns&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(s =&amp;gt; s);

   var routeData = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteData();
   var requestContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; RequestContext(httpContextMock.Object, routeData);
         
   &lt;span class="kwrd"&gt;this&lt;/span&gt;.Url = &lt;span class="kwrd"&gt;new&lt;/span&gt; UrlHelper(requestContext, routes);
}&lt;/pre&gt;
I'll be using&amp;nbsp;&lt;a href="http://msdn.microsoft.com/library/system.web.mvc.urlhelper"&gt;UrlHelper&lt;/a&gt;&amp;nbsp;to generate URLs because it has a nice API, but that doesn’t mean what I’m testing is specific to MVC, you could write the same tests using&amp;nbsp;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.getvirtualpath"&gt;RouteCollection.GetVirtualPath&lt;/a&gt;.

&lt;br /&gt;
&lt;h2&gt;




Name is optional&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; NameIsOptional() { 
         
   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;""&lt;/span&gt;);
}&lt;/pre&gt;
The route name is completely optional. I used to waste time trying to come up with a consistent naming convention and didn’t even use them.&lt;br /&gt;
&lt;h2&gt;




Parameters are required by default&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ParametersAreRequiredByDefault() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;);

   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { }));
   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; }), &lt;span class="str"&gt;"/b"&lt;/span&gt;);

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/"&lt;/span&gt;);

   Assert.IsNull(routes.GetRouteData(httpContextMock.Object));

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/b"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
You should know this one, so I won’t say much about it. When you include a parameter in the URL pattern, that parameter is required for both incoming requests and URL generation.&lt;br /&gt;
&lt;br /&gt;
In case you are not familiar with&amp;nbsp;&lt;a href="http://msdn.microsoft.com/library/system.web.mvc.urlhelper.routeurl"&gt;UrlHelper.RouteUrl&lt;/a&gt;,&amp;nbsp;&lt;code&gt;Url.Action("Index", "Home")&lt;/code&gt;&amp;nbsp;is equivalent to&amp;nbsp;&lt;code&gt;Url.RouteUrl(new { action = "Index", controller = "Home" })&lt;/code&gt;. I'm not using action and controller parameters to emphasize that: A. the tests are not specific to MVC; B. action and controller parameters are not special in any way.
&lt;br /&gt;
&lt;h2&gt;




Default value makes parameter optional&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DefaultValueMakesParameterOptional() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { }), &lt;span class="str"&gt;"/"&lt;/span&gt;);

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
The title says it all. You should know this one also.&lt;br /&gt;
&lt;h2&gt;




Can use null or empty string for parameter with default value&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CanUseNullOrEmptyStringForParameterWithDefaultValue() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)&lt;span class="kwrd"&gt;null&lt;/span&gt; }), &lt;span class="str"&gt;"/"&lt;/span&gt;);
   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }), &lt;span class="str"&gt;"/"&lt;/span&gt;);
}&lt;/pre&gt;
In the previous test we verified that when a parameter is optional you can omit its value in URL generation. Using null or an empty string also works.&lt;br /&gt;
&lt;br /&gt;
I like to take advantage of this one when generating URLs that refer to the default action of a controller, for example &lt;code&gt;Url.Action("")&lt;/code&gt; or &lt;code&gt;Url.Action("", "Account")&lt;/code&gt;. Note that for these to work the route &lt;b&gt;must&lt;/b&gt;&amp;nbsp;include the &lt;code&gt;{action}&lt;/code&gt; token, I explain why on the next test.&lt;br /&gt;
&lt;h2&gt;




Cannot use null or empty string for default value without parameter&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CannotUseNullOrEmptyStringForDefaultValueWithoutParameter() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"a"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"c"&lt;/span&gt; });

   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { b = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)&lt;span class="kwrd"&gt;null&lt;/span&gt; }));
   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;""&lt;/span&gt; }));
}&lt;/pre&gt;
This one is interesting. If you have a default value without a corresponding parameter, using null or empty string for that value in URL generation does not match the route.&lt;br /&gt;
&lt;br /&gt;
This is common when you have a route that is specific to a single controller or action, e.g.&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Products/Search"&lt;/span&gt;,
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Products"&lt;/span&gt;, action = &lt;span class="str"&gt;"Search"&lt;/span&gt; });&lt;/pre&gt;
&lt;h2&gt;




Value must match default value without parameter or be omitted&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ValueMustMatchDefaultValueWithoutParameterOrBeOmitted() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"a"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"c"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"c"&lt;/span&gt; }), &lt;span class="str"&gt;"/a"&lt;/span&gt;);
   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { }), &lt;span class="str"&gt;"/a"&lt;/span&gt;);
}&lt;/pre&gt;
In the previous test we verified that using null or empty string&amp;nbsp; does not work. Omitting the value or using the same value as the default works.&lt;br /&gt;
&lt;br /&gt;
Going back to the &lt;code&gt;"Products/Search"&lt;/code&gt; example, this test verifies that controller and action are constants that can either be omitted or provided with the same value to match the route. This is a nice feature that frees us from having to provide constraints for those values, e.g.&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Products/Search"&lt;/span&gt;,
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Products"&lt;/span&gt;, action = &lt;span class="str"&gt;"Search"&lt;/span&gt; },
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Products"&lt;/span&gt;, action = &lt;span class="str"&gt;"Search"&lt;/span&gt; }); &lt;span class="rem"&gt;// Constraints are not needed!&lt;/span&gt;&lt;/pre&gt;
&lt;h2&gt;




Constraint limits the value-space of a parameter&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ConstraintLimitsTheValueSpaceOfParameter() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b|c"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; }), &lt;span class="str"&gt;"/b"&lt;/span&gt;);
   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"c"&lt;/span&gt; }), &lt;span class="str"&gt;"/c"&lt;/span&gt;);
   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"d"&lt;/span&gt; }));

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/b"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/c"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/d"&lt;/span&gt;);

   Assert.IsNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
You know this one. String constraints are treated as regular expressions, which work on both incoming request and URL generation. You can also provide an &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.routing.irouteconstraint"&gt;IRouteConstraint&lt;/a&gt; instance, which gives you the option to choose when to apply it, incoming request, URL generation or both.&lt;br /&gt;
&lt;h2&gt;




Constraint tests the whole value&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ConstraintTestsTheWholeValue() {
         
   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; }), &lt;span class="str"&gt;"/b"&lt;/span&gt;);
   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b2"&lt;/span&gt; }));

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/b"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/b2"&lt;/span&gt;);

   Assert.IsNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
This is a nice feature. Whatever constraint you provide, the runtime wraps it in &lt;code&gt;^(yourconstraint)$&lt;/code&gt; for you, so it matches the whole value and not just a portion.&lt;br /&gt;
&lt;h2&gt;




Constraint for optional parameter should match an empty string if default value is empty string&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ConstraintForOptionalParameterShouldMatchAnEmptyStringIfDefaultValueIsEmptyString() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; });

   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }));

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/"&lt;/span&gt;);

   Assert.IsNull(routes.GetRouteData(httpContextMock.Object));

   &lt;span class="rem"&gt;//--------------------------------------&lt;/span&gt;

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"(b)?"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }), &lt;span class="str"&gt;"/"&lt;/span&gt;);

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));

   &lt;span class="rem"&gt;//--------------------------------------&lt;/span&gt;

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"b"&lt;/span&gt; });

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;""&lt;/span&gt; }), &lt;span class="str"&gt;"/"&lt;/span&gt;);

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
This one is in my view a design oversight, but not a big deal nonetheless. If you have a constraint for an optional parameter with an empty string default value, that constraint should match an empty string in case a value for that parameter is not provided. So, for instance if you have:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"Products/Browse/{sort}"&lt;/span&gt;,
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Products"&lt;/span&gt;, action = &lt;span class="str"&gt;"Browse"&lt;/span&gt;, sort = &lt;span class="str"&gt;""&lt;/span&gt; },
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { sort = &lt;span class="str"&gt;"popular|date"&lt;/span&gt; }); &lt;span class="rem"&gt;// Constraint should be "(popular|date)?"&lt;/span&gt;&lt;/pre&gt;
If a value for sort is not provided the default value is used, and the constraint &lt;code&gt;"popular|date"&lt;/code&gt; does not match an empty string, so it should be &lt;code&gt;"(popular|date)?"&lt;/code&gt;. This also applies if the default value is &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.urlparameter.optional"&gt;UrlParameter.Optional&lt;/a&gt;, or any value that returns an empty string from the &lt;code&gt;ToString()&lt;/code&gt; method. Ideally, the constraint would be ignored when the default value is an empty string.&lt;br /&gt;
&lt;br /&gt;
You won’t run into this issue for controller and action parameters, because you always provide non-empty defaults for those.&lt;br /&gt;
&lt;h2&gt;




Current request values are used as defaults&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CurrentRequestValuesAreUsedAsDefaults() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;);

   &lt;span class="kwrd"&gt;var&lt;/span&gt; routeData = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteData {
      Values = { 
         { &lt;span class="str"&gt;"a"&lt;/span&gt;, &lt;span class="str"&gt;"x"&lt;/span&gt; },
      }
   };

   &lt;span class="kwrd"&gt;var&lt;/span&gt; requestContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; RequestContext(&lt;span class="kwrd"&gt;this&lt;/span&gt;.Url.RequestContext.HttpContext, routeData);
   &lt;span class="kwrd"&gt;var&lt;/span&gt; Url = &lt;span class="kwrd"&gt;new&lt;/span&gt; UrlHelper(requestContext, routes);

   Assert.AreEqual(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { }), &lt;span class="str"&gt;"/x"&lt;/span&gt;);
}&lt;/pre&gt;
You can omit values when generating URLs, even for required parameters, and routing will use the current request values.&lt;br /&gt;
&lt;h2&gt;




UrlHelper.Action uses the current request controller and action values as defaults&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UrlHelperActionUsesTheCurrentRequestControllerAndActionValuesAsDefaults() {

   &lt;span class="kwrd"&gt;var&lt;/span&gt; routeData = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteData {
      Values = { 
         { &lt;span class="str"&gt;"controller"&lt;/span&gt;, &lt;span class="str"&gt;"b"&lt;/span&gt; },
         { &lt;span class="str"&gt;"action"&lt;/span&gt;, &lt;span class="str"&gt;"c"&lt;/span&gt; }
      }
   };

   &lt;span class="kwrd"&gt;var&lt;/span&gt; requestContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; RequestContext(&lt;span class="kwrd"&gt;this&lt;/span&gt;.Url.RequestContext.HttpContext, routeData);
   &lt;span class="kwrd"&gt;var&lt;/span&gt; Url = &lt;span class="kwrd"&gt;new&lt;/span&gt; UrlHelper(requestContext, routes);

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"a"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"b"&lt;/span&gt;, action = &lt;span class="str"&gt;"c"&lt;/span&gt; });

   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)&lt;span class="kwrd"&gt;null&lt;/span&gt;, action = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)&lt;span class="kwrd"&gt;null&lt;/span&gt; }));
   Assert.AreEqual(Url.Action(&lt;span class="kwrd"&gt;null&lt;/span&gt;), &lt;span class="str"&gt;"/a"&lt;/span&gt;);
   Assert.AreEqual(Url.Action(&lt;span class="kwrd"&gt;null&lt;/span&gt;, (&lt;span class="kwrd"&gt;string&lt;/span&gt;)&lt;span class="kwrd"&gt;null&lt;/span&gt;), &lt;span class="str"&gt;"/a"&lt;/span&gt;);
}&lt;/pre&gt;
The difference with this test and the previous test is that the previous is about ommiting values, this one is about using null values.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href="http://msdn.microsoft.com/library/system.web.mvc.urlhelper.action"&gt;UrlHelper.Action&lt;/a&gt;&amp;nbsp;set of methods take actionName and controllerName string parameters. Calling these methods can be translated to something equivalent to &lt;code&gt;Url.RouteUrl(new { action = actionName, controller = controllerName })&lt;/code&gt;. As we verified earlier in&amp;nbsp;&lt;i&gt;Cannot use null or empty string for default value without parameter&lt;/i&gt;, using null for actionName and/or controllerName can result in failing to match the desired route. For this reason &lt;code&gt;Url.Action&lt;/code&gt; uses the current request controller and action values whenever null is used for actionName and/or controllerName.&lt;br /&gt;
&lt;br /&gt;
This is a nice feature that allows us to generate URLs that refer to the current controller or action, e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;Url.Action(null)&lt;/code&gt; returns the URL of the current action.&lt;br /&gt;
&lt;code&gt;Url.Action("Foo")&lt;/code&gt; returns the URL of the Foo action in the current controller.&lt;br /&gt;
&lt;code&gt;Url.Action(null, "Bar")&lt;/code&gt; returns the URL of the action named like the current action, in the Bar controller.&lt;br /&gt;
&lt;h2&gt;




Routing is case insensitive&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RoutingIsCaseInsensitive() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"a/{b}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"A"&lt;/span&gt; }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"xyz"&lt;/span&gt; });

   Assert.IsNotNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"a"&lt;/span&gt;, b = &lt;span class="str"&gt;"XYZ"&lt;/span&gt; }));

   &lt;span class="kwrd"&gt;var&lt;/span&gt; httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/A/XYZ"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
Everything about routing is case insensitive, literal segments, default values and constraints.&lt;br /&gt;
&lt;h2&gt;




Routes are evaluated in order&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RoutesAreEvaluatedInOrder() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}/{b}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"c"&lt;/span&gt; });
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"{a}"&lt;/span&gt;);

   &lt;span class="kwrd"&gt;var&lt;/span&gt; httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/x"&lt;/span&gt;);

   Assert.IsTrue(
      Object.ReferenceEquals(
         routes.GetVirtualPath(Url.RequestContext, &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteValueDictionary(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"x"&lt;/span&gt; })).Route, 
         routes.First()
      )
   );

   Assert.IsTrue(
      Object.ReferenceEquals(
         routes.GetRouteData(httpContextMock.Object).Route, 
         routes.First()
      )
   );

   &lt;span class="kwrd"&gt;var&lt;/span&gt; lastRoute = routes.Last();
   routes.Remove(lastRoute);
   routes.Insert(0, lastRoute);

   Assert.IsTrue(
      Object.ReferenceEquals(
         routes.GetVirtualPath(Url.RequestContext, &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteValueDictionary(&lt;span class="kwrd"&gt;new&lt;/span&gt; { a = &lt;span class="str"&gt;"x"&lt;/span&gt; })).Route,
         routes.First()
      )
   );

   Assert.IsTrue(
      Object.ReferenceEquals(
         routes.GetRouteData(httpContextMock.Object).Route,
         routes.First()
      )
   );
}&lt;/pre&gt;
So far all tests involved a single route. If you have more than one route, the first one that matches is used, so the route registration order matters.&lt;br /&gt;
&lt;br /&gt;
The larger your application gets, the more routes you'll have, and you'll eventually run into issues where the route matched is not the one you want to match. To solve these issues you have two choices:&lt;br /&gt;
&lt;h3&gt;
1. Changing the order&lt;/h3&gt;
This tipically means putting the routes with the most parameters/segments at the top.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pros&lt;/b&gt;:
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Very easy to do.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;b&gt;Cons&lt;/b&gt;:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Can result in having the least used routes at the top, which is inneficient.&lt;/li&gt;
&lt;li&gt;Everytime you add a new route you have to test how it affects other routes.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
2. Adding constraints&lt;/h3&gt;
If a parameter should only match a limited set of values you can add a constraint for that parameter.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pros&lt;/b&gt;:
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;You don't have to worry (so much) about routes order.&lt;/li&gt;
&lt;li&gt;You can put the most used routes at the top.&lt;/li&gt;
&lt;li&gt;Reject bad URLs earlier.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;b&gt;Cons:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Everytime you create/remove a valid value for the parameter (e.g. add/remove controller/action) you have to update the constraint.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
This test has shown us that routing can get tricky. That's why people have created tools to debug these kind of issues, like &lt;a href="http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx"&gt;Route Debugger&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://getglimpse.com/"&gt;Glimpse&lt;/a&gt;. Mmm..., if only there was a tool that automated route creation and made me forget about these issues altogether... Wait!, &lt;a href="http://mvccoderouting.codeplex.com/"&gt;there is&lt;/a&gt;!&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;




Literal sub-segment bug&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;[TestMethod]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; LiteralSubsegmentBug() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"_{a}"&lt;/span&gt;);

   var httpContextMock = &lt;span class="kwrd"&gt;new&lt;/span&gt; Mock&amp;lt;HttpContextBase&amp;gt;();
   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/_b"&lt;/span&gt;);

   Assert.IsNotNull(routes.GetRouteData(httpContextMock.Object));

   httpContextMock.Setup(c =&amp;gt; c.Request.AppRelativeCurrentExecutionFilePath)
      .Returns(&lt;span class="str"&gt;"~/__b"&lt;/span&gt;);

   Assert.IsNull(routes.GetRouteData(httpContextMock.Object));
}&lt;/pre&gt;
You can read more about this bug &lt;a href="http://stackoverflow.com/questions/4318373"&gt;here&lt;/a&gt;. In short, literal sub-segments don't work very well, specially with string parameters. My recommendation is to use a constraint, and a custom type with a model binder to parse the parameter. For example, let's say you want to implement these URLs:&lt;br /&gt;
&lt;pre&gt;products/{id}
products/{slug}-{id}
&lt;/pre&gt;
Both should be handled by the same action, the &lt;code&gt;{id}&lt;/code&gt; part is required and &lt;code&gt;{slug}-&lt;/code&gt; is optional. First, we create a type that holds both pieces of data:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SlugIdentifier {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Pattern = &lt;span class="str"&gt;@"((.+)-)?([1-9]\d*)"&lt;/span&gt;;
   &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; Regex Regex = &lt;span class="kwrd"&gt;new&lt;/span&gt; Regex(Pattern);

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Id { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }
   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Slug { &lt;span class="kwrd"&gt;get&lt;/span&gt;; &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;set&lt;/span&gt;; }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; SlugIdentifier Parse(&lt;span class="kwrd"&gt;string&lt;/span&gt; identifier) {

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (identifier == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class="str"&gt;"identifier"&lt;/span&gt;);

      Match match = Regex.Match(identifier);

      &lt;span class="kwrd"&gt;if&lt;/span&gt; (!match.Success)
         &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentException(&lt;span class="str"&gt;"identifier is invalid."&lt;/span&gt;, &lt;span class="str"&gt;"identifier"&lt;/span&gt;);

      &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SlugIdentifier(
         Int32.Parse(match.Groups[3].Value),
         match.Groups[2].Value
      );
   }

   &lt;span class="kwrd"&gt;public&lt;/span&gt; SlugIdentifier(&lt;span class="kwrd"&gt;int&lt;/span&gt; id, &lt;span class="kwrd"&gt;string&lt;/span&gt; slug) {
      &lt;span class="kwrd"&gt;this&lt;/span&gt;.Id = id;
      &lt;span class="kwrd"&gt;this&lt;/span&gt;.Slug = slug;
   }
}&lt;/pre&gt;
Implementing the model binder is very easy:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; SlugIdentifierModelBinder : IModelBinder {

   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
      &lt;span class="kwrd"&gt;return&lt;/span&gt; SlugIdentifier.Parse(
         (&lt;span class="kwrd"&gt;string&lt;/span&gt;)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).RawValue
      );
   }
}&lt;/pre&gt;
And we register it on Application_Start:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterModelBinders(ModelBinderDictionary binders) {
   binders.Add(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(SlugIdentifier), &lt;span class="kwrd"&gt;new&lt;/span&gt; SlugIdentifierModelBinder());
}&lt;/pre&gt;
Finally, register the route:

&lt;br /&gt;
&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"products/{id}"&lt;/span&gt;,
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Products"&lt;/span&gt;, Action = &lt;span class="str"&gt;"Details"&lt;/span&gt; },
   &lt;span class="kwrd"&gt;new&lt;/span&gt; { id = SlugIdentifier.Pattern });&lt;/pre&gt;
And we are done!
&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Details(SlugIdentifier id) {
   ...
}&lt;/pre&gt;
&lt;h2&gt;




Two consecutive optional parameters bug&lt;/h2&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//[TestMethod] // Appears to be fixed in .NET 4.5&lt;/span&gt;
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TwoConsecutiveOptionalParametersBug() {

   routes.Clear();
   routes.MapRoute(&lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"a/{b}/{c}"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;""&lt;/span&gt;, c = &lt;span class="str"&gt;""&lt;/span&gt; });

   Assert.IsNotNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"1"&lt;/span&gt;, c = &lt;span class="str"&gt;"2"&lt;/span&gt; }));
   Assert.IsNotNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { b = &lt;span class="str"&gt;"1"&lt;/span&gt; }));
   Assert.IsNull(Url.RouteUrl(&lt;span class="kwrd"&gt;new&lt;/span&gt; { }));
}&lt;/pre&gt;
You can read more about this bug &lt;a href="http://haacked.com/archive/2011/02/20/routing-regression-with-two-consecutive-optional-url-parameters.aspx"&gt;here&lt;/a&gt;. I was able to reproduce in .NET 4, but not in .NET 4.5, so apparently it’s fixed.&lt;br /&gt;
&lt;h2&gt;






Conclusions&lt;/h2&gt;
&lt;div&gt;
I think I've covered everything you need to know about how routes are matched for incoming requests and URL generation.&lt;br /&gt;
&lt;br /&gt;
Routing is easy, if you have one route. The Routing system itself and ASP.NET MVC offer no help when it comes to detect issues like routes ambiguity. You have three choices: 1. unit-test your routes, 2. use debugging tools, 3. automate route creation.&lt;br /&gt;
&lt;br /&gt;
Thanks for reading!&lt;/div&gt;
</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/1714333762001795334/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/04/putting-aspnet-routing-to-test.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1714333762001795334?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/1714333762001795334?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/04/putting-aspnet-routing-to-test.html" title="Putting ASP.NET Routing to test" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;Dk8HSXozfyp7ImA9WhRaFko.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-5399727887710637749</id><published>2012-02-19T15:00:00.001-03:00</published><updated>2012-02-19T15:07:18.487-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-19T15:07:18.487-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="asp.net web pages" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>Web Pages exception in ASP.NET MVC 3 application after installing MVC 4 Beta</title><content type="html">After installing &lt;a href="http://www.asp.net/mvc/mvc4"&gt;ASP.NET MVC 4 Beta&lt;/a&gt;&amp;nbsp;I tried to run one of my MVC 3 applications and got this error:&lt;br /&gt;
&lt;blockquote class="tr_bq"&gt;
System.InvalidCastException: [A]System.Web.WebPages.Razor.Configuration.HostSection cannot be cast to [B]System.Web.WebPages.Razor.Configuration.HostSection. Type A originates from 'System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_1.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'. Type B originates from 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'.&lt;/blockquote&gt;
For some reason the framework was loading version 2.0 of System.Web.WebPages.Razor.dll instead of version 1.0. To fix it I just set&amp;nbsp;&lt;code&gt;webpages:Version&lt;/code&gt; to 1.0 in Web.config:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;appSettings&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;add&lt;/span&gt; &lt;span class="attr"&gt;key&lt;/span&gt;&lt;span class="kwrd"&gt;="webpages:Version"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;appSettings&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/5399727887710637749/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/webpages-exception-in-aspnet-mvc-3.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/5399727887710637749?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/5399727887710637749?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/webpages-exception-in-aspnet-mvc-3.html" title="Web Pages exception in ASP.NET MVC 3 application after installing MVC 4 Beta" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;DEIBQXo4eip7ImA9WhJTEk8.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-6747820127216383078</id><published>2012-02-18T13:18:00.001-03:00</published><updated>2012-06-20T17:09:10.432-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-06-20T17:09:10.432-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net web api" /><title>ASP.NET MVC 4 Beta: Web API is not MVC</title><content type="html">The &lt;a href="http://www.asp.net/mvc/mvc4"&gt;ASP.NET MVC 4 Beta&lt;/a&gt;&amp;nbsp;release includes a new API called &lt;a href="http://www.asp.net/web-api"&gt;ASP.NET Web API&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote class="tr_bq"&gt;
ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.&lt;/blockquote&gt;
You can find a better description in &lt;a href="http://weblogs.asp.net/jgalloway/archive/2012/02/16/asp-net-4-beta-released.aspx"&gt;this post&lt;/a&gt;. In short, Web&amp;nbsp;API is &lt;a href="http://wcf.codeplex.com/wikipage?title=WCF%20HTTP"&gt;WCF Web API&lt;/a&gt; rewritten in an MVC-ish way. It takes a lot of ideas from MVC, like Controllers, Routes, Model Metatada, Model Binding, Model Validation, Value Providers, Filter Attributes, etc., and adds WCF Web API features, like &lt;a href="http://www.odata.org/"&gt;OData&lt;/a&gt; and non-ASP.NET hosting.&lt;br /&gt;
&lt;br /&gt;
The bad thing is that, even though Web API looks and feels like MVC, &lt;b&gt;it's not MVC&lt;/b&gt;. All those great MVC features we all love, the Web API team decided to copy/re-implement, instead of building on top of MVC. &lt;b&gt;Web API does not depend on MVC&lt;/b&gt;. It doesn't even depend on ASP.NET.&lt;br /&gt;
&lt;br /&gt;
The best thing about MVC is it's extensibility. Now all those great MVC extensions will have to be recompiled/adapted for Web API. This is a terrible design choice, all they had to do was provide more RESTful features directly in MVC, instead of creating the Web API beast.&lt;br /&gt;
&lt;br /&gt;</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/6747820127216383078/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/aspnet-mvc-4-beta-web-api-is-not-mvc.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/6747820127216383078?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/6747820127216383078?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/aspnet-mvc-4-beta-web-api-is-not-mvc.html" title="ASP.NET MVC 4 Beta: Web API is not MVC" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DEUFQ307eSp7ImA9WhNRGEg.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-8458674420238774298</id><published>2012-02-14T01:12:00.000-03:00</published><updated>2012-11-13T22:23:32.301-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T22:23:32.301-03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="plugin" /><category scheme="http://www.blogger.com/atom/ns#" term="mvccoderouting" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net mvc" /><title>ASP.NET MVC Plugins</title><content type="html">There aren't many plugin solutions available for ASP.NET MVC, if you google &lt;i&gt;asp.net mvc plugins&lt;/i&gt; you'll find many proof-of-concept on how to load views embedded as assembly resources, but few established projects. Loading embedded views is only one part of the puzzle, first we should focus on routing.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;
Routing&lt;/h2&gt;
&lt;a href="http://portableareas.codeplex.com/"&gt;MvcContrib Portable Areas&lt;/a&gt;&amp;nbsp;solution to routing for plugins is a very simple one: Plugin = Area. And it works. All you have to do is:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Add a reference to the portable area assembly (or copy to 'bin').&lt;/li&gt;
&lt;li&gt;Call&amp;nbsp;&lt;code&gt;AreaRegistration.RegisterAllAreas()&lt;/code&gt; on&amp;nbsp;Application_Start.&lt;/li&gt;
&lt;/ol&gt;
The problem with this solution is &lt;b&gt;you are not in control of the URL space used by the plugin&lt;/b&gt;. For example, Portable Areas includes a sample called LoginPortableArea. All routes defined by this area start with &lt;code&gt;Login/&lt;/code&gt;, and, apparently, you cannot change that (unless, of course, you recompile the plugin). What if you don't want to use &lt;code&gt;Login/&lt;/code&gt;, but &lt;code&gt;Account/&lt;/code&gt; or &lt;code&gt;User/&lt;/code&gt; instead?. What if you can't use &lt;code&gt;Login/&lt;/code&gt;, because it's already being used in your application?&lt;br /&gt;
&lt;br /&gt;
With &lt;a href="http://mvccoderouting.codeplex.com/"&gt;MvcCodeRouting&lt;/a&gt;&amp;nbsp;&lt;b&gt;you have complete control of the URL space used by the plugin&lt;/b&gt;. This is how you register a plugin:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterRoutes(&lt;span class="type"&gt;RouteCollection&lt;/span&gt; routes) {
   
   routes.MapCodeRoutes(
      baseRoute: &lt;span class="str"&gt;"Account"&lt;/span&gt;,
      rootController: &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MvcAccount.&lt;span class="type"&gt;AccountController&lt;/span&gt;),
      settings: &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;CodeRoutingSettings&lt;/span&gt; { 
         EnableEmbeddedViews = &lt;span class="kwrd"&gt;true&lt;/span&gt;
      }
   );
}&lt;/pre&gt;
The &lt;code&gt;baseRoute&lt;/code&gt; parameter specifies where you want to expose the plugin, it can be anything you want, even null, which means the plugin is exposed at the root of the application.&amp;nbsp;The &lt;code&gt;rootController&lt;/code&gt;&amp;nbsp;parameter specifies the type of the controller that is the entry point for the plugin. MvcCodeRouting also create routes for other controllers in the same assembly. The &lt;code&gt;settings&lt;/code&gt; parameter is used to customize the way routes are created, and to enable embedded views.&lt;br /&gt;
&lt;br /&gt;
MvcCodeRouting is not a plugin-specific solution, you can use it to automatically create routes for your own controllers. In fact, the only plugin-related feature is views embedding.&lt;br /&gt;
&lt;h2&gt;

Views&lt;/h2&gt;
MvcCodeRouting&amp;nbsp;uses a custom &lt;a href="http://msdn.microsoft.com/library/system.web.hosting.virtualpathprovider"&gt;VirtualPathProvider&lt;/a&gt;&amp;nbsp;to load embedded views. This is an optional feature, if you want you can just copy all views to your application. You can also override embedded views using file views in your application. How does it work?&amp;nbsp;The VirtualPathProvider implementation presents a merged view (no pun intended) of file and embedded views. When a view exists in both file system and the assembly, the file view takes precedence. This is how you enable&amp;nbsp;embedded views:&lt;br /&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterViewEngines(&lt;span class="type"&gt;ViewEngineCollection&lt;/span&gt; engines) {
   engines.EnableCodeRouting();
}&lt;/pre&gt;
&lt;h2&gt;

How to create a plugin using MvcCodeRouting&lt;/h2&gt;
For MvcCodeRouting a plugin is &lt;b&gt;an assembly with controllers, and optionally embedded views&lt;/b&gt;. That's all. In fact, if your plugin routes are simple enough (&lt;code&gt;{controller}/{action}&lt;/code&gt; or &lt;code&gt;{controller}/{action}/{id}&lt;/code&gt;) you don't even need to reference the MvcCodeRouting assembly. You can use hierarchical (a.k.a. RESTful) and custom routes by decorating your code with attributes.&lt;br /&gt;
&lt;br /&gt;
View resources must be named using the following format: &lt;code&gt;{assemblyName}.Views.{viewPath}&lt;/code&gt;. That means you just need to create a 'Views' folder in root of your plugin project and put the views there (and set Build Action to 'Embedded Resource', of course):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-Dh1J8EpCwXg/TznJODMeH9I/AAAAAAAAAng/SWSQHqi-x0c/s1600/mvcaccount_embedded_views.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-Dh1J8EpCwXg/TznJODMeH9I/AAAAAAAAAng/SWSQHqi-x0c/s320/mvcaccount_embedded_views.png" width="287" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;

Conclusions&lt;/h2&gt;
MvcCodeRouting makes it really easy to create and use plugins. The application developer (plugin user) is always in control of the URL space used by plugins, and can individually override views as needed. The plugin developer does not need to worry about how the plugin is exposed or how views are located.&lt;br /&gt;
&lt;h2&gt;

Where to go next&lt;/h2&gt;
&lt;a href="http://mvccoderouting.codeplex.com/"&gt;mvccoderouting.codeplex.com&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://mvcaccount.codeplex.com/"&gt;mvcaccount.codeplex.com&lt;/a&gt;</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/8458674420238774298/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/aspnet-mvc-plugins.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8458674420238774298?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/8458674420238774298?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/aspnet-mvc-plugins.html" title="ASP.NET MVC Plugins" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-Dh1J8EpCwXg/TznJODMeH9I/AAAAAAAAAng/SWSQHqi-x0c/s72-c/mvcaccount_embedded_views.png" height="72" width="72" /><thr:total>5</thr:total></entry><entry gd:etag="W/&quot;DkQCRHs-eCp7ImA9WhRaEEU.&quot;"><id>tag:blogger.com,1999:blog-1017037604230733988.post-5056529383356181117</id><published>2012-02-12T19:06:00.000-03:00</published><updated>2012-02-12T19:06:05.550-03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-12T19:06:05.550-03:00</app:edited><title>Hello World 2.0</title><content type="html">This is my second blog attempt. After falling in love with &lt;a href="http://stackoverflow.com/"&gt;StackOverflow&lt;/a&gt; I decided I didn't need my (first) blog anymore, SO provided a much richer interaction. When I had a question I would just ask it on SO. If I had an answer, I would look for the question, if found and no answer looked like mine I would post my answer. A couple of times I post a question just to post my own answer, but that didn't feel right. With time SO got stricter on what kind of questions were allowed. Then came &lt;a href="http://programmers.stackexchange.com/"&gt;Programmers&lt;/a&gt;, but never really connected with it. I don't understand why that site, created for programming questions not suitable for SO, has so many closed questions. What's next? Programmers 2? I missed the freedom to just post anything I wanted, no rules, no scores, so here I am again. This time I'm much more skilled and experienced, so I hope to connect with a larger audience.</content><link rel="replies" type="application/atom+xml" href="http://maxtoroq.blogspot.com/feeds/5056529383356181117/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/hello-world-20.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/5056529383356181117?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1017037604230733988/posts/default/5056529383356181117?v=2" /><link rel="alternate" type="text/html" href="http://maxtoroq.blogspot.com/2012/02/hello-world-20.html" title="Hello World 2.0" /><author><name>Max Toro</name><uri>http://www.blogger.com/profile/14721989987840636890</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/-Ib9LKcU5fAU/TzhVAf8KkFI/AAAAAAAAAm0/QKv-RlRGSek/s1600/1056a38ecd323c658a8c9e93bb41b18d" /></author><thr:total>0</thr:total></entry></feed>
