<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Jeff&amp;#39;s Junk : MVC</title><link>http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx</link><description>Tags: MVC</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Dependency injection, providers and ASP.NET MVC</title><link>http://weblogs.asp.net/jeff/archive/2009/09/16/dependency-injection-providers-and-asp-net-mvc.aspx</link><pubDate>Wed, 16 Sep 2009 15:23:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7208223</guid><dc:creator>Jeff</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7208223</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/09/16/dependency-injection-providers-and-asp-net-mvc.aspx#comments</comments><description>&lt;p&gt;I've been thinking a lot about all of the frameworks we have now to use with our, uh, frameworks. There's a framework to solve every problem. Dependency injection frameworks are of particular interest to a lot of people because they make unit testing ridiculously easy. They're also well suited to something like ASP.NET MVC, where you're trying to make as few dependencies as possible between the various concerns.&lt;/p&gt;&lt;p&gt;But I was chatting with someone the other day about all of the frameworks for DI, and he expressed concern that he wasn't comfortable depending on a lot of external libraries for some things. His view (pun intended) was that you start to junk up a relatively simple framework like MVC by putting all kinds of other stuff into it to support another framework, and that introduces different kinds of risks. For example...&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You're decorating everything with attributes.&lt;/li&gt;&lt;li&gt;There are all kinds of configuration files or classes to do the hookup.&lt;/li&gt;&lt;li&gt;You'll replace the standard controller factories, perhaps giving new devs one more thing to learn about for your project.&lt;/li&gt;&lt;li&gt;Global.asax becomes even more of a dumping ground (more because of the placement of routing there plus DI framework init).&lt;/li&gt;&lt;li&gt;It just feels dirty to some to be using another library you don't own.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I think these are all valid concerns, though the scope of them depends a lot on your specific scenario. The funny thing about using frameworks that are named after design patterns is that everything becomes an academic debate about where and how you do this and that. And if you're a junior developer wanting to impress your peers, or a senior developer not wanting to set a precedent for the "wrong" thing, you want to get it "right."&lt;/p&gt;&lt;p&gt;Many of the examples published on the Web and in books suggest simply using different constructors on MVC controllers to handle DI in a rational way that doesn't require an additional framework. That allows you to keep the default controller factory and let your unit tests instantiate them with your mock objects. I read a lengthy discussion forum thread that suggested this wasn't proper either because you aren't testing the way that the objects are created in the real production environment. I thought that was a pretty thin argument, but I do see the point.&lt;/p&gt;&lt;p&gt;I got to thinking about how the provider model introduced in ASP.NET 2.0 worked pretty well for a lot of things, and it was used effectively before that in wiring up swappable data access layers on various apps. Creating a lightweight container to do that wire-up would be pretty straight forward, even if it did reinvent some wheels.&lt;/p&gt;&lt;p&gt;I don't really have a point that I'm after here. I guess these are things I'd like to hear people talk more about. What are you doing in the real world to keep things testable and maintainable?&lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7208223" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/unit+testing/default.aspx">unit testing</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>Under the covers of HTML helpers in ASP.NET MVC</title><link>http://weblogs.asp.net/jeff/archive/2009/08/18/under-the-covers-of-html-helpers-in-asp-net-mvc.aspx</link><pubDate>Tue, 18 Aug 2009 18:14:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7172549</guid><dc:creator>Jeff</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7172549</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/08/18/under-the-covers-of-html-helpers-in-asp-net-mvc.aspx#comments</comments><description>&lt;p&gt;Coming from the Webforms world, the thing you might miss most when using ASP.NET MVC is the ability to create rich Web controls that generate all kinds of markup and do nifty things. HTML helpers do similar work, even if their plumbing is different, and because we can see the full source code of the MVC framework, we can explore their innards. Keep in mind that this isn't a straight analog, since there are no events to worry about. The helpers have one responsibility, and that's to display the right data as HTML. The truth is that you don't need to know any of this, as the existing helpers probably meet your needs 95% of the time. But in the event you want to build your own helpers for the purpose of encapsulating some kind of common, reusable markup (or keep your views cleaner), you'll benefit from understanding what goes on inside the black box.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;By the way, if you've never looked, I'd strongly encourage you to download and poke around the &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=53289097-73ce-43bf-b6a6-35e00103cb4b&amp;amp;displaylang=en" target="_blank" mce_href="http://www.microsoft.com/downloads/details.aspx?FamilyID=53289097-73ce-43bf-b6a6-35e00103cb4b&amp;amp;displaylang=en"&gt;MVC source code&lt;/a&gt;. I know that you're probably expecting a big mess of overly abstract pieces that you can't follow, but it's one of the cleanest and easiest to understand projects I've ever seen. They did a really great job with this, and the whole thing weighs in at only 10,000 lines of code, and 500 of that is just resource stuff.&lt;/p&gt;

&lt;p&gt;HTML helpers generally do the dumb work of creating markup. They save you the hassle of making input tags or links or whatever. But the other thing they do, the magic, if you will, is process model state. &lt;code&gt;Controller&lt;/code&gt; has a property called &lt;code&gt;ModelState&lt;/code&gt;, which is an instance of the &lt;code&gt;ModelStateDictionary&lt;/code&gt; class. This is passed along to the view in a context object. Your views inherit from &lt;code&gt;ViewPage&lt;/code&gt;, which has an instance of &lt;code&gt;HtmlHelper&lt;/code&gt; (the &lt;code&gt;Html&lt;/code&gt; property). Got all that? I'm first trying to illustrate how data gets from the controller to the view, where the helpers do their thing. The controller is where you process input via model binding and validate, and this is what gets the model state involved. It's a different topic from what we're discussing here, but hopefully you've had exposure to it and understand the goodness that makes working with input and models so easy. (Read up on &lt;a href="http://www.asp.net/mvc/learn/" target="_blank" mce_href="http://www.asp.net/mvc/learn/"&gt;the tutorials on the official site&lt;/a&gt;, specifically the stuff about model binders and validation.)&lt;br&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;HtmlHelper&lt;/code&gt; class by itself doesn't do much to generate markup. It does have the anti-forgery token code in it, as well as some static methods to create route links (I'm not sure I understand the decision to put them there), but otherwise, the creation of markup lies in several other classes filled with extension methods to &lt;code&gt;HtmlHelper&lt;/code&gt;, found in the &lt;code&gt;System.Web.Mvc.Html&lt;/code&gt; namespace. They're grouped into classes for input, forms, links, selection controls, validation, etc. Since they're all extension methods, they have access to the data in the &lt;code&gt;HtmlHelper&lt;/code&gt; instance (the &lt;code&gt;Html&lt;/code&gt; property of the view), so that means they can work with the model state and virtually anything else available in the view. As extension methods, you can hopefully see that you can build your own to generate most any HTML you can think of.&lt;/p&gt;

&lt;p&gt;Let's start with the simplest example: text boxes. Found in the &lt;code&gt;InputExtensions&lt;/code&gt; class, there are several overloads:&lt;/p&gt;

&lt;code&gt;public static string TextBox(this HtmlHelper htmlHelper, string name) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return TextBox(htmlHelper, name, null /* value */);&lt;br&gt;}&lt;br&gt;&lt;br&gt;public static string TextBox(this HtmlHelper htmlHelper, string name, object value) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return TextBox(htmlHelper, name, value, (object)null /* htmlAttributes */);&lt;br&gt;}&lt;br&gt;&lt;br&gt;public static string TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return TextBox(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));&lt;br&gt;}&lt;br&gt;&lt;br&gt;public static string TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary&amp;lt;string, object&amp;gt; htmlAttributes) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return InputHelper(htmlHelper, InputType.Text, name, value, (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);&lt;br&gt;}&lt;/code&gt;

&lt;p&gt;The MVC team is obviously thinking of your well being in allowing you to provide as much, or little, as you need to make that text box. The real meat comes in the last overload, which calls another extension method in the class named &lt;code&gt;InputHelper&lt;/code&gt;. This is where we finally start to make some HTML.&lt;/p&gt;

&lt;p&gt;Before we get into that method, consider what the typical code looks
like in your view to make a text box. It may look something like this
in a strongly typed view that has a "Name" property on the Model:&lt;/p&gt;

&lt;code&gt;&amp;lt;%= Html.TextBox("Name", null, new { @class = "textField" })%&amp;gt;&lt;/code&gt;

&lt;p&gt;This says, "Create a text box on the 'Name' property, don't give it a
value, and while you're at it, add a class attribute with the value
'textField'." This corresponds to the third overload of the available
extension methods. In essence, we're calling:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;InputHelper(Html /* instance of HtmlHelper on the view */, InputType.Text, "Name", null, (null ==
null) /* useViewData */, false /* isChecked */, true /* setId */, true
/* isExplicitValue */, new { @class = "textField" });&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pretty well-factored code! This works with the extension methods for check boxes, hidden
fields, passwords and radio buttons. Note the fifth parameter, the
Boolean useViewData parameter. What this says in English is, "If the
value given is null, then use the view data."&lt;/p&gt;

&lt;p&gt;Here's the &lt;code&gt;InputHelper&lt;/code&gt; code, with some of the non-relevant parts taken out for the purpose of this example.&lt;/p&gt;
&lt;code&gt;private static string InputHelper(this HtmlHelper htmlHelper, InputType inputType, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, IDictionary&amp;lt;string, object&amp;gt; htmlAttributes) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (String.IsNullOrEmpty(name)) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TagBuilder tagBuilder = new TagBuilder("input");&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.MergeAttributes(htmlAttributes);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.MergeAttribute("name", name, true);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; bool usedModelState = false;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; switch (inputType) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case InputType.CheckBox:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case InputType.Radio:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; case InputType.Password:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; default:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (setId) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.GenerateId(name);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // If there are any errors for a named field, we add the css attribute.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ModelState modelState;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (modelState.Errors.Count &amp;gt; 0) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return tagBuilder.ToString(TagRenderMode.SelfClosing);&lt;br&gt;}&lt;/code&gt;
&lt;p&gt;Now we can dig in. To get things started, the method creates a &lt;code&gt;TagBuilder&lt;/code&gt; object, which is used across most (maybe all) of the helper extension methods. Check out the source if you get a chance. It encapsulates the process of creating the tag and its attributes, and does all of the boring string formatting work. It creates an ID, checks for duplicate attributes, sets the innards of a non-self-closing tag, etc. The one important thing to note is that it also concatenates CSS classes (important for the validation part, as we'll see in a minute).&lt;/p&gt;

&lt;p&gt;The switch block trickles down to the default, where it uses the &lt;code&gt;GetModelStateValue&lt;/code&gt; method of the &lt;code&gt;HtmlHelper&lt;/code&gt; to see if there is a value to drop into the text box. That method returns the value from the model if it's there and matches the type String (since we're using text here), otherwise it returns null. The next line calls up the &lt;code&gt;TagBuilder&lt;/code&gt; to add a value attribute to our tag. What it puts there depends on what's available. You may recall from the documentation or books that the first thing it tries to do is use the value stored in ViewData.ModelState, if that's available, and that's what the &lt;code&gt;EvalString&lt;/code&gt; method of &lt;code&gt;HtmlHelper&lt;/code&gt; does. If that's not available, it goes with the value we passed in from the markup in the view. Since we specified null, it won't show anything, even if there was no value in model state. Again, read up on how model state is used to work with validation to repopulate a view with the values entered before posting the form to the controller.&lt;/p&gt;&lt;p&gt;After creating an id attribute for the tag, the method finally gets to the validation piece. Recall that fields that don't validate have their helpers render the CSS class "input-validation-error" in addition to any style classes you've specified. That happens here by checking to see if there are any errors associated with the model state of this field.&lt;/p&gt;&lt;p&gt;Finally, the method uses &lt;code&gt;TagBuilder&lt;/code&gt; to build out the string of HTML to drop into the view. If you look around at the other helper methods, you'll find that they all work similarly to this.&lt;/p&gt;&lt;p&gt;To summarize, the helpers do this work:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Use &lt;code&gt;TagBuilder&lt;/code&gt; to create the HTML tag&lt;/li&gt;&lt;li&gt;Pass in the attributes&lt;/li&gt;&lt;li&gt;Figure out what the value of the HTML control will be&lt;/li&gt;&lt;li&gt;Give the tag an id&lt;/li&gt;&lt;li&gt;If there are validation errors, apply the predetermined CSS class in addition to others specified&lt;/li&gt;&lt;li&gt;Output the string of HTML into the view&lt;/li&gt;&lt;/ul&gt;As I said before, I think the code is surprisingly easy to follow, and once you understand what's going on under the covers, and how it interacts with model state, you're in a good position to start working your own helpers. &lt;br&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7172549" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>Book project follow up</title><link>http://weblogs.asp.net/jeff/archive/2009/08/10/book-project-follow-up.aspx</link><pubDate>Mon, 10 Aug 2009 18:03:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7165942</guid><dc:creator>Jeff</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7165942</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/08/10/book-project-follow-up.aspx#comments</comments><description>&lt;p&gt;I'm still getting e-mail about the book I started to write and posted chapters for. Now that ScottGu has formally announced the progress on v2 of ASP.NET MVC, hopefully it's more obvious now why I put off the project. As I said previously, I had to prioritize with some other projects that would yield income sooner, and I was concerned with the speed to market with which I could get it out there (read: not fast enough). I also knew about the second iteration in the pipe, which made it a no-brainer to put it off.&lt;/p&gt;&lt;p&gt;As I said, that content will find some kind of home eventually. I still think that the mid-level experience developers aren't being served adequately, especially the tens of thousands in small shops, indie contractors, etc. I'm still trying to get my head around what that means and what the solution might be. If you're a part of that group, trying to latch on to the latest and greatest, I'd like to hear from you! &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7165942" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/writing/default.aspx">writing</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>Webforms vs. MVC, the desire to rewrite everything, an unexpected benefit</title><link>http://weblogs.asp.net/jeff/archive/2009/06/23/webforms-vs-mvc-the-desire-to-rewrite-everything-an-unexpected-benefit.aspx</link><pubDate>Wed, 24 Jun 2009 02:16:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7133018</guid><dc:creator>Jeff</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7133018</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/23/webforms-vs-mvc-the-desire-to-rewrite-everything-an-unexpected-benefit.aspx#comments</comments><description>&lt;p&gt;I read a good post today about the silly wars that go on in versus debates, in this case &lt;a href="http://weblogs.asp.net/tonylombardo/archive/2009/06/23/mvc-vs-webforms-a-clear-loser-emerging.aspx" target="_blank" mce_href="http://weblogs.asp.net/tonylombardo/archive/2009/06/23/mvc-vs-webforms-a-clear-loser-emerging.aspx"&gt;the arguments about whether to use Webforms or MVC for ASP.NET&lt;/a&gt;.
I kind of saw this storm coming when people started describing
themselves as part of the alternative "movement" in the ASP.NET
community.&lt;/p&gt;&lt;p&gt;The truth is that MVC gets me really excited, for a
thousand different reasons. My win comes from the fact that I feel like
I'm no longer fighting the statelessness of the Web by conforming to a
framework that simulates statefullness. Maybe I never really realized I
was doing that. But the nice thing about MVC is that it has forced me
to break down what I'm doing into simple actions. There are two
benefits developing that I had not previously considered.&lt;/p&gt;&lt;p&gt;The
first is that the stripped down nature of the MVC pattern causes you to
back off if you're someone who does UI development. It can be argued
that many of the apps out there now that get all of the press and
attention are ridiculously simple and easy to use because they don't
try to do a lot, and, by no coincidence, happen to be developed on
another MVC framework, Rails. Whether it be Twitter or my wife's
favorite site, Ravelry, there is a simple style that seems
representative of sites developed using this design pattern.&lt;/p&gt;&lt;p&gt;The
second benefit is that adhering to a strict seperation of concerns,
whether it be for reasons of testability or the desire to decouple your
pieces parts as much as possible (or because "they" said so), makes
your code infinitely easier to maintain.&lt;/p&gt;&lt;p&gt;That said, when bouncing
back to a Webforms project, I'm amazed at just how much cleaner
everything I write is in terms of keeping the moving parts minimally
coupled to each other. It's not that I had 10,000-line code-behind
files before, but my mindset has changed enough since spending time in
MVC that I'm a better developer now when using Webforms.&lt;/p&gt;&lt;p&gt;And when
all of the crazy zealots go on and on about how awesome MVC is, I'm not
writing that image gallery control for the hundredth time, because I
did it once years ago. The simple content management apps, handlers and
diagnostic pieces are all there still for me to use, and now I'm wiring
it all together in a much cleaner way.&lt;/p&gt;&lt;p&gt;I love MVC. I mean I really dig it. I can't wait to get some of my projects based on it out into the open (other than &lt;a href="http://jeffputz.com/" target="_blank" mce_href="http://weblogs.asp.net/"&gt;my personal blog&lt;/a&gt;,
anyway). But the truth is that the value already built into years of
ASP.NET Webforms is not trivialized or bested by the new framework.
Instead, there's a great compliment in place now where I can use which
ever gets me to market the fastest. At the end of the day, rewriting
things for the sake of science doesn't get me paid. Shipping stuff is
what puts food on the table. Now my toolbox is a lot bigger.&lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7133018" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/culture/default.aspx">culture</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC book: Not going to happen (for now)</title><link>http://weblogs.asp.net/jeff/archive/2009/06/09/asp-net-mvc-book-not-going-to-happen-for-now.aspx</link><pubDate>Wed, 10 Jun 2009 02:51:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7115407</guid><dc:creator>Jeff</dc:creator><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7115407</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/09/asp-net-mvc-book-not-going-to-happen-for-now.aspx#comments</comments><description>&lt;p&gt;After a great deal of soul searching (&lt;a href="http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx" target="_blank" mce_href="http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx"&gt;and a PDF draft of chapter 1 posted&lt;/a&gt;), I've decided that I'm not going to follow through on this book. I've got a total of four chapters, two of which are at 75%, but there are a number of reasons that I've decided to focus my attention elsewhere.&lt;/p&gt;&lt;p&gt;The biggest issue I have right now is that I don't see an obvious personal benefit to following through at this time, especially when I compare it to other projects. Since I'm essentially "self-employed," and foresee that as a lasting condition for some time given the sad job market, I really want to pour more energy into things that look and smell like income. Self-publishing, for all of the benefits, doesn't pay up front the way an advance does through a mainstream publisher and, you know, I like to eat and do summer stuff.&lt;/p&gt;&lt;p&gt;I've also had a lot of fun in the last two months tweaking and analyzing my existing Web sites. &lt;a href="http://coasterbuzz.com/" target="_blank" mce_href="http://coasterbuzz.com/"&gt;CoasterBuzz&lt;/a&gt; is doing very well, with page views up 20%. I've been shooting video and photographing rides for the site too, something I love to do but kind of let go of over the years. I'm really getting back in touch with the stuff that motivated me to get into software in the first place.&lt;/p&gt;&lt;p&gt;Perhaps most important, I'm getting back into some basic level of fitness. I'm married again, and I have to look after myself for someone else's benefit as well. The nasty high stress crappy job I lost really had me pushing aside just common sense eating and exercise, and I want to make sure I'm balancing all of that out.&lt;/p&gt;&lt;p&gt;The content I've written so far will not go away, and it will see the light of day one way or another. Perhaps it'll appear as a finished book for a future release, or as a series of long Web pieces. I haven't decided. The simple blogging app used as the example code will be released as well, probably sooner than later (in part because I'm hell bent on proving that 95% of what you want to do can be achieved by 10% of the average "best practice" or "sample" app).&lt;/p&gt;&lt;p&gt;This wasn't an easy decision, especially given all of the wonderful feedback I've received so far (and even some outright hate mail). At the end of the day, I need to chase what gets me out of bed, and unfortunately, this just wasn't it. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7115407" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>Personal blog moved, powered by ASP.NET MVC</title><link>http://weblogs.asp.net/jeff/archive/2009/06/04/personal-blog-moved-powered-by-asp-net-mvc.aspx</link><pubDate>Fri, 05 Jun 2009 00:08:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7108379</guid><dc:creator>Jeff</dc:creator><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7108379</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/04/personal-blog-moved-powered-by-asp-net-mvc.aspx#comments</comments><description>&lt;p&gt;I moved my personal blog to a more appropriate domain name, &lt;a href="http://jeffputz.com/" target="_blank" mce_href="http://jeffputz.com/"&gt;JeffPutz.com&lt;/a&gt;. Hooray for vanity names! I'll continue to keep technical and programming junk here. It seemed appropriate though to have a central location for me stuff, including my resume and various media samples, so this is it.&lt;br&gt;&lt;/p&gt;&lt;p&gt;This is the first MVC app that I've put into production, oddly enough. Well, technically it is, though I've supervised the development of MVC apps elsewhere. This little app is what I've been writing my next book around, and it is purposefully as simple as possible. Most of the sample apps I've tried to disect are entirely too complex, and I think that's a barrier for a lot of people. I'll post the code after I've put it through the paces a bit. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7108379" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC book: Chapter 1: Plumbing</title><link>http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx</link><pubDate>Fri, 08 May 2009 18:27:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7078816</guid><dc:creator>Jeff</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7078816</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx#comments</comments><description>&lt;p&gt;ASP.NET MVC: From Webforms to MVC &lt;br&gt;by Jeff Putz &lt;br&gt;&amp;nbsp;&lt;br&gt;The following is a barely-edited draft from the forthcoming book &lt;br&gt;that will explore Microsoft’s ASP.NET MVC framework from the &lt;br&gt;view of a traditional Webforms developer. This chapter goes over &lt;br&gt;the basic plumbing of the framework, while subsequent chapters &lt;br&gt;will be more focused on the typical use cases that every developer &lt;br&gt;encounters, and how they relate to their Webforms analogs. &lt;br&gt;&amp;nbsp;&lt;br&gt;Again, this is a draft, so read with caution. You can find updates on &lt;br&gt;the status of the book, which will likely be published early summer, &lt;br&gt;2009, at the following locations: &lt;br&gt;&amp;nbsp;&lt;br&gt;http://weblogs.asp.net/Jeff/ &lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;a href="http://twitter.com/jeffputz" target="_blank" mce_href="http://twitter.com/jeffputz"&gt;http://twitter.com/jeffputz&lt;/a&gt;&lt;/p&gt;
&lt;p style="font-size: 16px;"&gt;&lt;b&gt;&lt;a href="http://popw.com/corp/media/aspnetmvc-ch1.pdf" mce_href="http://popw.com/corp/media/aspnetmvc-ch1.pdf"&gt;Download a more formatted version in PDF form... it's much easier to read! &lt;/a&gt;&lt;/b&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The text is ©2009, Jeff Putz, all rights reserved.&lt;/p&gt;
&lt;p&gt;Feedback is welcome, especially if you find something that is outright incorrect. Thanks for reading! Did I mention the PDF version is easier to read?&lt;/p&gt;&lt;p&gt;EDIT, Previously:&lt;br&gt;&lt;a href="http://weblogs.asp.net/jeff/archive/2009/04/30/asp-net-mvc-from-webforms-to-mvc-introduction.aspx" mce_href="http://weblogs.asp.net/jeff/archive/2009/04/30/asp-net-mvc-from-webforms-to-mvc-introduction.aspx"&gt;Introduction&lt;/a&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;The M, The V, The C and Other Plumbing &lt;br&gt;&amp;nbsp;&lt;br&gt;We explained in the introduction what MVC is, the model-view-controller &lt;br&gt;pattern. Let’s get to a quick and dirty example to see it in action. &lt;br&gt;&amp;nbsp;&lt;br&gt;Hello kittehs &lt;br&gt;&amp;nbsp;&lt;br&gt;As a tribute to those illiterate captioning cats on the Internet, why don’t we &lt;br&gt;quickly build an application that gets the names of cats and displays them. We’ll &lt;br&gt;fire up Visual Studio, choose a new ASP.NET MVC Web Application from the &lt;br&gt;File -&amp;gt; New Project menu, and get to work immediately. For now, we’ll skip &lt;br&gt;creating a test project. &lt;br&gt;&lt;a href="http://weblogs.asp.net/blogs/jeff/fig%201.1.png"&gt;&lt;img src="http://weblogs.asp.net/blogs/jeff/fig%201.1.png" mce_src="http://weblogs.asp.net/blogs/jeff/fig%201.1.png" align="right" border="0"&gt;&lt;/a&gt; &lt;br&gt;If we take a peak in the &lt;br&gt;solution explorer, we’ll see &lt;br&gt;all kinds of goodies created &lt;br&gt;for us. You’ll immediately &lt;br&gt;notice that there are several &lt;br&gt;folders that correspond with &lt;br&gt;the three facets of our &lt;br&gt;pattern, Models, Views and &lt;br&gt;Controllers. This is &lt;br&gt;where the app expects to &lt;br&gt;find the pieces by &lt;br&gt;convention, much in the &lt;br&gt;same way that it expects to &lt;br&gt;find compiled assemblies in &lt;br&gt;the /bin folder or themes in &lt;br&gt;/App_Themes. &lt;br&gt;&amp;nbsp;&lt;br&gt;You’ll also notice some &lt;br&gt;other folders for content and &lt;br&gt;scripts. The former is for &lt;br&gt;things like images and style &lt;br&gt;sheets, the latter is intended &lt;br&gt;for scripts. This project &lt;br&gt;template is good enough to &lt;br&gt;add both the jQuery library &lt;br&gt;and the ASP.NET AJAX &lt;br&gt;files for you. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;Figure 1.1: The new MVC project generated &lt;br&gt;by Visual Studio. &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;--sidebar&lt;/p&gt;
&lt;p&gt;You might be wondering if you’re stuck putting all of your code in &lt;br&gt;this structure, but rest assured, it’s not required. Most of the teams &lt;br&gt;that I’ve worked with break out all of their code into one or more &lt;br&gt;library projects, compiled into their own assemblies. All of your &lt;br&gt;model and controller code can go in separate projects, just make sure &lt;br&gt;that the MVC project references the other projects. You do lose the &lt;br&gt;convention-based association between controllers and views (we’ll &lt;br&gt;go over that momentarily), but that’s far from being a deal breaker. &lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;--/sidebar&lt;/p&gt;
&lt;p&gt;If you look a little closer, you’ll also notice that the Views folder has subfolders &lt;br&gt;that correspond to the names of the C# files found in Controllers. This allows &lt;br&gt;you to return views without having to explicitly choose them. That will be &lt;br&gt;obvious once we flesh out our simple example. &lt;br&gt;&amp;nbsp;&lt;br&gt;The Views folder contains .aspx files that make the views. They also may contain &lt;br&gt;.ascx files, which are known as partial views. They act somewhat like the user &lt;br&gt;controls you’re used to in Webforms, but they’re used in a different way. &lt;br&gt;Furthermore, it’s critical to understand that these views are not a one-to-one &lt;br&gt;mapping with URL’s in your app. There is no /views/home/index.aspx to view. If &lt;br&gt;you look at the web.config file found in the Views folder, you’ll notice an &lt;br&gt;HttpHandler is set up to block the direct viewing of these files entirely. &lt;br&gt;&amp;nbsp;&lt;br&gt;Before you start scratching your head, think back to our first description of how &lt;br&gt;MVC works. Incoming requests are routed to controllers, which based on some &lt;br&gt;logic, return a particular view. The missing link is the routing table, which takes &lt;br&gt;the URL of the request and hands it off to a controller. The routing table is &lt;br&gt;established in global.asax, so let’s take a peek at what the project template has &lt;br&gt;put there. The default is shown in Listing 1.1. &lt;br&gt;&amp;nbsp;&lt;br&gt;In the world of MVC, global.asax is used as the location for establishing the &lt;br&gt;route table, a special collection object used to map requests to controllers. The &lt;br&gt;RouteTable class has a single static property, Routes, of type &lt;br&gt;RouteCollection, used to store these mappings. It has the typical methods for &lt;br&gt;manipulating a collection, and a class of extension methods &lt;br&gt;(RouteCollectionExtensions) adds two other important methods, &lt;br&gt;IgnoreRoute() and MapRoute(). These do exactly what you expect, forcing &lt;br&gt;the MVC routing engine to ignore certain URL’s and send others to certain &lt;br&gt;controllers. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;--sidebar&lt;br&gt;&amp;nbsp;&lt;br&gt;This is a very quick overview on routing. ASP.NET MVC is &lt;br&gt;extremely extensible, and you can do a lot with custom routes, &lt;br&gt;constraints, custom view engines, etc. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;--/sidebar &lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;Listing 1.1: The default global.asax file &lt;br&gt;&amp;nbsp;&lt;br&gt;using System.Web.Mvc; &lt;br&gt;using System.Web.Routing; &lt;br&gt;&amp;nbsp;&lt;br&gt;namespace HelloKittehs &lt;br&gt;{ &lt;br&gt;&amp;nbsp; public class MvcApplication : System.Web.HttpApplication &lt;br&gt;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void RegisterRoutes(RouteCollection &lt;br&gt;routes) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; routes.MapRoute( &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Default",&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Route name &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "{controller}/{action}/{id}", // URL with parameters &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new {controller = "Home", action = "Index", id = ""} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Parameter defaults &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ); &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void Application_Start() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RegisterRoutes(RouteTable.Routes); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The first line of our RegisterRoutes() method tells the engine to ignore the &lt;br&gt;.axd pieces typically found in an ASP.NET application, like script handlers and &lt;br&gt;the trace page. The second one sets up the default route, using one of several &lt;br&gt;overloads. The comments generated explain what each parameter does. The first &lt;br&gt;is a name for the route, in this case, “Default.” The second parameter outlines the &lt;br&gt;URL structure by naming parameters that will establish the route, including the &lt;br&gt;controller name, the action to call on the controller and an ID to pass along. The &lt;br&gt;final parameter creates an anonymous type with the default values for the route &lt;br&gt;parameters. Following this convention, a request for “/home/getcat/3” will route &lt;br&gt;the request to the HomeController class, call its GetCat action method and &lt;br&gt;pass in “3” as a parameter. &lt;/p&gt;
&lt;p&gt;--sidebar&lt;/p&gt;
&lt;p&gt;&lt;br&gt;Not familiar with anonymous types? They’re a key feature of C# 3.0 &lt;br&gt;and essential to using LINQ, and to serialize JSON for AJAX calls. &lt;br&gt;An anonymous type creates a class with the properties you declare, &lt;br&gt;without the formal declaration of a named class. The anonymous &lt;br&gt;type in Listing 1.1 creates a class with controller, action and id &lt;br&gt;properties. &lt;/p&gt;
&lt;p&gt;--/sidebar&lt;br&gt;&lt;/p&gt;
&lt;p&gt;One final thing to note in the default generated code, is the presence of a &lt;br&gt;Default.aspx page. Its code behind class creates and executes an instance of &lt;br&gt;the MvcHttpHandler class, which according to the routes just established will &lt;br&gt;execute the HomeController and call its Home method, since those are the &lt;br&gt;default values we established. That code is there so Internet Information Services &lt;br&gt;(IIS) knows what to do with a request that has no path. &lt;br&gt;&amp;nbsp;&lt;br&gt;Let’s get to our simple app. Delete the three folders under Views, but leave the &lt;br&gt;web.config in place. Delete the AccountController.cs file under &lt;br&gt;Controllers. Now that we have a clean start, right-click the Models folder and &lt;br&gt;add a new class file called KittehNames.cs. We’re going to write a &lt;br&gt;ridiculously simple method to return names, and you can see it Listing 1.2. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.2: KittehNames.cs &lt;br&gt;&amp;nbsp;&lt;br&gt;namespace HelloKittehs.Models &lt;br&gt;{ &lt;br&gt;&amp;nbsp; public class KittehNames &lt;br&gt;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static string[] Get() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string[] names = new [] {"Cosmo", "Emma", "Gideon", &lt;br&gt;"Oliver"}; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return names; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;There isn’t anything remarkable about this method at all. It exists solely to return &lt;br&gt;an array of strings. Naturally you could also return an array or List&amp;lt;T&amp;gt; of &lt;br&gt;objects that offered more detail, like the cat’s gender or age, but we’ll keep it &lt;br&gt;simple with strings. &lt;br&gt;&amp;nbsp;&lt;br&gt;Let’s move on to the controller. So as not to change that which has been &lt;br&gt;generated for us, we’ll use the HomeController class, but remove the About() &lt;br&gt;method. We’ll replace the contents of the Index() method and the result will &lt;br&gt;look like Listing 1.3. &lt;br&gt;&amp;nbsp;&lt;br&gt;Requests routed to the HomeController class, taking the Index action, will &lt;br&gt;execute the Index() method. Controller actions return ActionResult objects, &lt;br&gt;which come in many derived forms, but for now we’ll stick to the ViewResult. &lt;br&gt;The System.Web.Mvc.Controller base class includes helper methods to &lt;br&gt;return results, including the View() method, which we have in our action here. &lt;br&gt;&amp;nbsp;&lt;br&gt;The only line we have added is one to retrieve the cat names, and add them to &lt;br&gt;ViewData property, a dictionary objet and member of the Controller base class. &lt;br&gt;Returning a call to the View() method, with no parameter, tells the controller to &lt;br&gt;look for a view in the Views folder, under a subfolder that matches the name of &lt;br&gt;the controller, Home, and an actual view file that matches the name of the action, &lt;br&gt;in this case Index.aspx. If it can’t find the view in the path &lt;br&gt;“/Views/Home/Index.aspx,” it will look in “/Views/Shared/Index.aspx.” These &lt;br&gt;naming conventions are built in to the MVC framework. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.2: KittehNames.cs &lt;br&gt;&amp;nbsp;&lt;br&gt;using System.Web.Mvc; &lt;br&gt;using HelloKittehs.Models; &lt;br&gt;&amp;nbsp;&lt;br&gt;namespace HelloKittehs.Controllers &lt;br&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp; public class HomeController : Controller &lt;br&gt;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ActionResult Index() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ViewData["AllKittehNames"] = KittehNames.Get(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return View(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;We deleted all of the views, so let’s create one now. To create one that follows &lt;br&gt;the naming conventions, right-click on our call to the View() method, as shown &lt;br&gt;in Figure 1.2. Uncheck the boxes and click OK. &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Figure 1.2: Adding a view from a controller action. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;a href="http://weblogs.asp.net/blogs/jeff/fig%201.2.png"&gt;&lt;img src="http://weblogs.asp.net/blogs/jeff/fig%201.2.png" border="0"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;The view is created with the path “/Views/Home/Index.aspx,” because it’s being &lt;br&gt;called by the HomeController class from the Index() action. Let’s fill it out &lt;br&gt;with Listing 1.3. The bold piece is new.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Listing 1.3: /Views/Home/Index.aspx &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %&amp;gt; &lt;br&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 &lt;br&gt;Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- &lt;br&gt;transitional.dtd"&amp;gt; &lt;br&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt; &lt;br&gt;&amp;lt;head runat="server"&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt; &lt;br&gt;&amp;lt;/head&amp;gt; &lt;br&gt;&amp;lt;body&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;div&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% foreach (var name in ViewData["AllKittehNames"] as &lt;br&gt;string[]) { %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;%= name %&amp;gt;&amp;lt;br /&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% } %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;/div&amp;gt; &lt;br&gt;&amp;lt;/body&amp;gt; &lt;br&gt;&amp;lt;/html&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;This view is about as simple as it gets. We’ve added a loop to cycle through all of &lt;br&gt;the values from the ViewData entry we added in the controller. Note that we use &lt;br&gt;the as keyword to indicate the type of the dictionary entry we’re calling. If the &lt;br&gt;object can’t be cast to the type indicated, it’s treated as a null. This is different &lt;br&gt;from a straight cast, which will throw an exception when the cast fails. That’s an &lt;br&gt;important consideration when choosing how you perform the cast. &lt;br&gt;&amp;nbsp;&lt;br&gt;Note the subtle, yet different syntax for executing code and for resolving a value. &lt;br&gt;Code execution happens inside of the &amp;lt;% %&amp;gt; block, while values are resolved and &lt;br&gt;output from the &amp;lt;%= %&amp;gt; block. &lt;br&gt;&amp;nbsp;&lt;br&gt;Figure 1.3: The default view, rendered. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;a href="http://weblogs.asp.net/blogs/jeff/fig%201.3.png"&gt;&lt;img src="http://weblogs.asp.net/blogs/jeff/fig%201.3.png" border="0"&gt;&lt;/a&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;If you run the site, you’ll get the results in your browser shown in Figure 1.3. &lt;br&gt;Because of the routing set up in global.asax, you can point the browser to &lt;br&gt;“/home/index” as well. &lt;br&gt;&amp;nbsp;&lt;br&gt;To recap, here’s what happens when the request is made to the server: &lt;br&gt;&amp;nbsp;&lt;br&gt;• The routing to a controller and action is determined. In the case of &lt;br&gt;“/home/index,” or the root of the site (which is handled by &lt;br&gt;Default.aspx), the routing table determines that MVC should &lt;br&gt;instantiate the HomeController class and call its Index() method. &lt;br&gt;The naming convention take the “home” and adds “Controller” to &lt;br&gt;determine the class name of the controller. &lt;br&gt;&amp;nbsp;&lt;br&gt;• The framework makes an instance of the HomeController, calling its &lt;br&gt;default constructor. We did not explicitly write one, but the compiler &lt;br&gt;makes it for us. &lt;br&gt;&amp;nbsp;&lt;br&gt;• The correct action method is called. The method must return a derivative &lt;br&gt;of ActionResult. In our example, the Index() method is called. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Logic is performed as needed in the action method, but it should be &lt;br&gt;limited to handling input or, in this case, preparing data for output in the &lt;br&gt;view. &lt;br&gt;&amp;nbsp;&lt;br&gt;• The action method returns a view for rendering to the browser. Without &lt;br&gt;any parameters, we’re creating an instance of a view by naming &lt;br&gt;convention. &lt;br&gt;&amp;nbsp;&lt;br&gt;• The view executes its display logic, and uses data sent to it by the &lt;br&gt;controller. &amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The great thing about this arrangement is the separation of concerns we’ve been &lt;br&gt;talking about from the start. The model, view and controller can all execute &lt;br&gt;without having to know anything about the internal processes of each other. This &lt;br&gt;is especially helpful for testing purposes, and we’ll get deeper into that later. &lt;br&gt;&amp;nbsp;&lt;br&gt;Models &lt;br&gt;&amp;nbsp;&lt;br&gt;The model part of MVC in the strictest sense refers to the data infrastructure for &lt;br&gt;your application. It’s the stateful part of your app, where real things are stored. In &lt;br&gt;the broader sense, the Models folder of your MVC app ends up acting much like &lt;br&gt;/App_Code, because your application logic goes here. It can also live in external &lt;br&gt;libraries, compiled projects that have no dependency on your application. &lt;br&gt;&amp;nbsp;&lt;br&gt;The most common set of tutorials you’ll see on models for MVC involve the &lt;br&gt;creation of data entities that map to a database technology, usually SQL Server. &lt;br&gt;We’ll go deeper into that in the next chapter. &lt;br&gt;&amp;nbsp;&lt;br&gt;However, it’s important to note that one of the key benefits to using ASP.NET &lt;br&gt;MVC is the ability to test the snot out of as much of your application as possible. &lt;br&gt;In that sense, solid designs that code against interfaces, combined with mocking &lt;br&gt;frameworks, make it easier to test your model code. Stay tuned for more on that. &lt;br&gt;&amp;nbsp;&lt;br&gt;Controllers &lt;br&gt;&amp;nbsp;&lt;br&gt;The System.Web.Mvc.Controller class is the basis for the magic glue that &lt;br&gt;bridges application logic and data (models) with the presentation of a user &lt;br&gt;interface (view). You’ve already seen how a request is mapped to a specific &lt;br&gt;controller. It should also be obvious that the controller must inherit from the &lt;br&gt;Controller base class. &lt;br&gt;&amp;nbsp;&lt;br&gt;By convention, these controllers must be named with the “Controller” suffix. So &lt;br&gt;or example, requests routed to the “Admin” controller require that the controller &lt;br&gt;class be named AdminController. &lt;br&gt;&amp;nbsp;&lt;br&gt;The second part of the routing equation calls a specific action method on the &lt;br&gt;controller class. These methods must have a return type of ActionResult. This &lt;br&gt;abstract class has ten derivatives (if you count the PartialViewResult). You &lt;br&gt;don’t need to create specific instances of these result classes, but can use the &lt;br&gt;helper methods that come free with the Controller class: &lt;br&gt;&amp;nbsp;&lt;br&gt;• View() – This is the most common helper you’ll use, as it returns &lt;br&gt;instances of the views that you create. It has several overloads, the &lt;br&gt;simplest being one that takes no parameters. It will look for a view that &lt;br&gt;matches the name of the action. So if your action is named Cat and your &lt;br&gt;controller class is AdminController, it will look for a view called &amp;nbsp;&lt;br&gt;Cat.aspx in /Views/Admin. If it can’t find it there, it will look in &lt;br&gt;/Views/Shared. There are additional overloads that allow you to &lt;br&gt;return a specific view and even specify a certain master page. By &lt;br&gt;passing an object in to View(), you can bind data to a strongly typed &lt;br&gt;view, as we’ll see in the next section. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Redirect(), RedirectToAction() and RedirectToRoute() – &lt;br&gt;This set of methods and their various overloads do exactly what the &lt;br&gt;names imply. There are times when you don’t want to return a view, but &lt;br&gt;send the user to some other location. The regular Redirect() is about &lt;br&gt;the same as Response.Redirect() in Webforms. The other two take &lt;br&gt;a variety of parameters to send the user to a specific destination based &lt;br&gt;on the controller, action and route you specify. The helpful angle is that &lt;br&gt;you don’t need to know what the actual URL is, and if you change the &lt;br&gt;routing, you won’t have a hard-coded (and broken) URL in your code. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Json() – The secret sauce to quick AJAX enriched Web apps is &lt;br&gt;JavaScript Object Notation, or JSON. This light-weight data format is &lt;br&gt;deal for sending short messages back to the client, and requires no &lt;br&gt;special parsing or processing in the browser. You can pass in any &lt;br&gt;serializable object to this method. We’ll be talking about it extensively &lt;br&gt;in a later chapter. &lt;br&gt;&amp;nbsp;&lt;br&gt;• JavaScript() – Returns, believe it or not, some JavaScript. &lt;br&gt;&amp;nbsp;&lt;br&gt;• File() – This one has some of the goodness you would expect from a &lt;br&gt;straight IHttpHandler in the Webforms world. You can pass it &lt;br&gt;Stream objects and byte arrays and specify the MIME type of the &lt;br&gt;response. This is ideal for returning images or documents. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Content() – This is the best choice for returning string content. It too &lt;br&gt;allows you to specify the MIME type. &lt;br&gt;&amp;nbsp;&lt;br&gt;We can limit the kinds of requests that come in to controller actions with special &lt;br&gt;attributes, but we’ll cover those along with the use cases that match their use. &lt;br&gt;&amp;nbsp;&lt;br&gt;The Controller class also has several properties to get at the objects you would &lt;br&gt;typically find as part of System.Web.UI.Page, like Request, Response, &lt;br&gt;HttpContext and User. &lt;/p&gt;
&lt;p&gt;--sidebar&lt;/p&gt;
&lt;p&gt;As is the case with a lot of classes in the framework, there are many &lt;br&gt;members to explore, and we’re not going to mention them all. &lt;br&gt;Fortunately, they’re easy to Google by simply searching for the &lt;br&gt;class, like “system.web.mvc.controller class.” It’s even faster if you &lt;br&gt;have http://msdn.microsoft.com/ bookmarked.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;--/sidebar &lt;br&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;br&gt;Views &lt;br&gt;&amp;nbsp;&lt;br&gt;The views you create with .aspx file names are not pages, in the strict sense of &lt;br&gt;the word. You should think of them as, well, views on data! Several different &lt;br&gt;URL’s can eventually route to controllers and actions that serve up the same &lt;br&gt;views, so there is not a one-to-one relationship between the URL and the view. &lt;br&gt;&amp;nbsp;&lt;br&gt;At the very least, a view is any HTML that you choose. Using a series of helpers, &lt;br&gt;we can cause additional markup to be rendered around data that we pass through &lt;br&gt;to a view from a controller action. This results in what appears at first to look like &lt;br&gt;classic ASP, but it’s not. As a rule, there should be absolutely no logic in the &lt;br&gt;page unless it has something to do with the display (or validation) of data. You &lt;br&gt;would never calculate tax or make a database call from a view. On the other &lt;br&gt;hand, it’s OK to decide whether or not to display a block of HTML based on &lt;br&gt;whether or not the user is logged in. &lt;br&gt;&lt;br&gt;--sidebar&lt;/p&gt;
&lt;p&gt;Hardcore computer science people may engage in some level of &lt;br&gt;debate about what data is OK to act on. For example, checking &lt;br&gt;Request.IsAuthenticated definitely isn’t rooted in the data populated &lt;br&gt;by the controller, but it’s a common sense way to do things. My &lt;br&gt;advice: It’s a not a religion, so if it doesn’t violate the separation of &lt;br&gt;concerns by creating a difficult dependency, don’t lose sleep over it. &lt;/p&gt;
&lt;p&gt;--/sidebar&lt;/p&gt;
&lt;p&gt; &lt;br&gt;HTML helpers create form tags, user interface elements, links and other useful &lt;br&gt;stuff for rendering in your views. The class used for this is &lt;br&gt;System.Web.Mvc.HtmlHelper, augmented by several classes full of extension &lt;br&gt;methods. Because all view pages inherit from the ViewPage class, you can use &lt;br&gt;its Html property to access these helpers. In the next chapter, we’ll demonstrate &lt;br&gt;how many of these helpers are used. &lt;br&gt;&amp;nbsp;&lt;br&gt;There are also a great many methods for use in the AjaxHelper class and its &lt;br&gt;extension methods. &lt;br&gt;&amp;nbsp;&lt;br&gt;While ViewPage does inherit from the traditional Page class, most of what you &lt;br&gt;find there is not relevant to an MVC view. The HTML helpers and straight forms &lt;br&gt;do not participate in the event or postback model, so avoid getting into the events &lt;br&gt;you’re used to using in Webforms. &lt;br&gt;&amp;nbsp;&lt;br&gt;There are several new properties that you will want to take note of. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Html – As mentioned previously, this property accesses the wealth of &lt;br&gt;the HtmlHelper class and its various extensions. &lt;br&gt;&amp;nbsp;&lt;br&gt;• ViewData – Remember our simple example that rendered cat names? &lt;br&gt;This property is your one-stop access to the data assigned in the &lt;br&gt;controller. &lt;br&gt;&amp;nbsp;&lt;br&gt;• ViewContext – This property give you access to ViewContext, which &lt;br&gt;is a derivative of ControllerContext. It gives you access to the &lt;br&gt;Controller, HttpContext, RequestContext, RouteData and &lt;br&gt;TempData. Be careful when accessing these objects so as not to create &lt;br&gt;any dependencies on specific controllers. &lt;br&gt;&amp;nbsp;&lt;br&gt;• Ajax – Provides access to the AjaxHelper class and its extensions. &lt;br&gt;&amp;nbsp;&lt;br&gt;Views can also be strongly typed. ViewPage and ViewUserConrol (the class &lt;br&gt;used for partial views, later in this chapter) have a generic descendent that tie the &lt;br&gt;view to a specific model. Visual Studio will automatically set this up for you in &lt;br&gt;the create view dialog, which we’ll use later. We can rewrite our original view &lt;br&gt;and controller action method from HelloKitteh as shown in Listing 1.4 &lt;br&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.4: Strongly typing the view, and passing a model to it &lt;br&gt;&amp;nbsp;&lt;br&gt;HomeController.cs &lt;br&gt;namespace HelloKittehs.Controllers &lt;br&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp; public class HomeController : Controller &lt;br&gt;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ActionResult Index() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var names = KittehNames.Get(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return View(names); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;/Views/Home/Index.aspx &lt;br&gt;&amp;lt;%@ Page Language="C#" &lt;br&gt;Inherits="System.Web.Mvc.ViewPage&amp;lt;String[]&amp;gt;" %&amp;gt; &lt;br&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 &lt;br&gt;Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- &lt;br&gt;transitional.dtd"&amp;gt; &lt;br&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt; &lt;br&gt;&amp;lt;head runat="server"&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt; &lt;br&gt;&amp;lt;/head&amp;gt; &lt;br&gt;&amp;lt;body&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;div&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% foreach (var name in Model) { %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;%= name %&amp;gt;&amp;lt;br /&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% } %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt; &lt;br&gt;&amp;lt;/body&amp;gt; &lt;br&gt;&amp;lt;/html&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The changes we made are in bold, as compared to Listings 1.2 and 1.3. We’re &lt;br&gt;passing an object to the controller’s View() method, which is designated as the &lt;br&gt;model for the view. The view itself inherits from a generic version of ViewPage, &lt;br&gt;in this case, ViewPage&amp;lt;String[]&amp;gt;. You can use any type you like here, &lt;br&gt;including complex types that encapsulate any number of different data items. If &lt;br&gt;our view were more complex, the Intellisense in the page would generate &lt;br&gt;whatever properties and methods are found on the model when we type a period &lt;br&gt;after it, because the Model property is an alias to the type you’ve designated in &lt;br&gt;the @Page declaration. &lt;/p&gt;
&lt;p&gt;--sidebar&lt;/p&gt;
&lt;p&gt;The cool thing going on here is that instead of a dictionary object &lt;br&gt;like ViewData, we’re enforcing some very specific contracts &lt;br&gt;between the controller and the view. Pulling stuff from the &lt;br&gt;dictionary isn’t as safe and there’s no warning at compile time about &lt;br&gt;whether or not the data will be there. It makes testing a lot easier too, &lt;br&gt;because your controller methods return strongly typed data. That &lt;br&gt;said, it may not be appropriate for every case, and you can still use a &lt;br&gt;combination of a strongly typed view and ViewData.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;--/sidebar &lt;br&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Under the hood of ASP.NET MVC &lt;br&gt;&amp;nbsp;&lt;br&gt;Let’s step back for a moment to talk about what’s going on under the hood, &lt;br&gt;especially since we glossed over the notion that there is no one-to-one &lt;br&gt;relationship between URL’s and views. &lt;br&gt;&amp;nbsp;&lt;br&gt;In a traditional Webforms application, a request comes into the Web server, &lt;br&gt;Internet Information Server (IIS), and does one of two things, depending on the &lt;br&gt;version of IIS. If it’s IIS 6, the server determines that the incoming request &lt;br&gt;should be handled by ASP.NET, usually because of the file extension, like &lt;br&gt;.aspx. If it’s IIS 7, ASP.NET already has access to everything coming in. &lt;br&gt;&amp;nbsp;&lt;br&gt;Next, ASP.NET checks web.config’s HttpHandlers section to see what class &lt;br&gt;should handle the request. If no entry is found, it checks at the machine level, &lt;br&gt;which says that .aspx requests will be handed off to &lt;br&gt;System.Web.UI.PageHandlerFactory, a class that implements the &lt;br&gt;IHttpHandlerFactory interface. This class looks at the URL of the request, &lt;br&gt;and serves up the appropriate instance of a class that implements &lt;br&gt;IHttpHandler. As you may guess, this will be your page, which inherits from &lt;br&gt;the Page class (which implements IHttpHandler). From there, all of the &lt;br&gt;familiar things happen inside your page. &lt;br&gt;&amp;nbsp;&lt;br&gt;If you’ve never implemented an IHttpHandler class yourself, the premise is &lt;br&gt;pretty simple: Make something to send back to the browser. At its most basic &lt;br&gt;level, it provides access to the stream that is sent out by the server to the client. &lt;br&gt;One of the most common uses that I’ve encountered is to send the bytes that &lt;br&gt;together make an image that is stored in a database. You set the MIME type, put &lt;br&gt;the bytes in the output stream, and enjoy. &lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;--sidebar&lt;br&gt;&lt;/p&gt;
&lt;p&gt;It’s true, you can do the same thing in the Load event of a page, but &lt;br&gt;you’re creating all of that overhead and baggage that comes with &lt;br&gt;creating a page. Remember, a page is just a really robust &lt;br&gt;HttpHandler. It’s easier and more efficient to strip it down and &lt;br&gt;implement your own for something like serving an image.&lt;/p&gt;
&lt;p&gt;--/sidebar&lt;/p&gt;
&lt;p&gt; Understanding how a request is handled, you’re empowered even in Webforms to &lt;br&gt;break the notion that a request must map to an actual file. You might build an &lt;br&gt;application with a handler that intercepts URL’s like “/news/371.aspx” and &lt;br&gt;creates an instance of the page of your choosing using &lt;br&gt;System.Web.UI.PageParser.GetCompiledPageInsance(). At that point, &lt;br&gt;there’s no going back to thinking of your app as a series of pages, but rather an &lt;br&gt;application! &lt;br&gt;&amp;nbsp;&lt;br&gt;It’s not hard to figure out what’s going on with MVC once you have this &lt;br&gt;knowledge either. The differences are, however, noticeable between the two &lt;br&gt;versions of IIS. In IIS 6, an .mvc file extension is mapped to ASP.NET, and &lt;br&gt;handled by an HttpHandler specified in web.config. The reason for this is that &lt;br&gt;IIS 6 by default treats ASP.NET as a subsystem that runs as an ISAPI module &lt;br&gt;(Internet Server Application Programming Interface). We’re not going to get into &lt;br&gt;it here, but as the name slightly implies, it’s a way to plug in pretty much &lt;br&gt;anything into the server’s request/response pipeline. So now a request ending in &lt;br&gt;.mvc is routed to ASP.NET, and new entries in web.config in turn map it to the &lt;br&gt;right handler. If you want to work without these nasty file extensions, you can &lt;br&gt;simply set the wildcard map in IIS 6 to send all requests to ASP.NET. &lt;br&gt;&amp;nbsp;&lt;br&gt;IIS 7 integrates ASP.NET much closer to the Ethernet port on the server, so to &lt;br&gt;speak. That means that configuration files are considered immediately. The &lt;br&gt;system.webServer section of web.config allows you to adjust the plumbing &lt;br&gt;without having to set configuration properties in the IIS control panel. &lt;br&gt;&amp;nbsp;&lt;br&gt;This probably has you wondering how ASP.NET MVC knows how to handle a &lt;br&gt;request with no particular file extension. Enter the HttpModule! These classes &lt;br&gt;implement IHttpModule, and have access to many events in the &lt;br&gt;request/response lifecycle, set at the application level. Back in the early days of &lt;br&gt;ASP.NET this interface is what you used to implement your own logic to assign &lt;br&gt;roles to user on each request. You might use it today to implement logic that &lt;br&gt;indicates a user’s subscription status. And because it has access to the Error &lt;br&gt;event, you can build an error logging module here too. &lt;br&gt;&amp;nbsp;&lt;br&gt;ASP.NET MVC uses System.Web.Routing.UrlRoutingModule to look at &lt;br&gt;the request, compare it to the routing table, and find the right IRouteHandler &lt;br&gt;object for the route, the default being the MvcRouteHandler. IRouteHandlers &lt;br&gt;are intended to return an IHttpHandler (which is not specific to MVC, thus the &lt;br&gt;inclusion in the System.Web.Routing namespace), and MvcRouteHandler &lt;br&gt;returns an instance of MvcHandler. That class loads an instance of the &lt;br&gt;appropriate Controller derivative, which fires off an action. Got all that? One &lt;br&gt;of the important points here is that the whole system is extensible, and starting at &lt;br&gt;the top, with the routing table, you can specify alternate route handlers. &lt;br&gt;&amp;nbsp;&lt;br&gt;More on routing &lt;br&gt;&amp;nbsp;&lt;br&gt;You’ll recall in our trivial example that global.asax declares a default route to &lt;br&gt;follow: &lt;br&gt;&amp;nbsp;&lt;br&gt;routes.MapRoute( &lt;br&gt;&amp;nbsp;&amp;nbsp; "Default",&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Route name &lt;br&gt;&amp;nbsp;&amp;nbsp; "{controller}/{action}/{id}", // URL with parameters &lt;br&gt;&amp;nbsp;&amp;nbsp; new {controller = "Home", action = "Index", id = ""} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Parameter defaults &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ); &lt;br&gt;&amp;nbsp;&lt;br&gt;This route says the first component in the path of the URL will name the &lt;br&gt;controller, the second will name the action to take, and the third will name an ID &lt;br&gt;of some kind. When any of them are missing, from right to left, the defaults &lt;br&gt;specified in the third parameter are used. Thus, no path in the URL would call the &lt;br&gt;Index method on the HomeController, a URL of “/Admin” will call the &lt;br&gt;Index method of the AdminController, and if no ID is specified, it will try &lt;br&gt;first to call an action method with no parameters. &lt;br&gt;&amp;nbsp;&lt;br&gt;Chances are good that you don’t want to be constrained to this one routing &lt;br&gt;situation. You could have a blogging app (oh, wait, we have one of those!), &lt;br&gt;where the path should be “/post/some-post-name,” but using the default route, &lt;br&gt;this maps to the PostController class calling an action method of some- &lt;br&gt;post-name, which obviously would not exits. This is a case for a custom route, &lt;br&gt;which we’ve typed out in Listing 1.5. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.5: Adding a custom route to global.asax &lt;br&gt;&amp;nbsp;&lt;br&gt;routes.MapRoute("PostRoute", "blog/{id}", &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; new {controller = "Post", action = "Detail"}); &lt;br&gt;&amp;nbsp;&lt;br&gt;routes.MapRoute("Default", "{controller}/{action}/{id}", &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; new {controller = "Home", action = "Index", id = ""}); &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The first thing you’ll notice is that we explicitly spell out the first part of the &lt;br&gt;URL, with “blog” followed by the slash and the “id” in brackets. Because we’re &lt;br&gt;not getting a controller name or action from the URL, we specify the one to use &lt;br&gt;in the default object parameter, in this case the Detail action method of the &lt;br&gt;PostController. &lt;br&gt;&amp;nbsp;&lt;br&gt;As we’ll find later in more detailed examples, the kind of data you want to pass &lt;br&gt;to an action varies. In some cases, passing a string where the method wants an &lt;br&gt;integer will simply throw an exception (like “/Admin/Edit/foo” when the method &lt;br&gt;is Edit(int id), for example). That may or may not be adequate for your &lt;br&gt;exception handling skills. But there are also cases where you may actually &lt;br&gt;require one or the other. Fortunately, you can pass a fourth parameter into the &lt;br&gt;MapRoute() method that indicates a regular expression that the request must &lt;br&gt;conform to. If you only wanted to match blog entries by an integer ID, you would &lt;br&gt;map the route as indicated in Listing 1.6. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.6: Adding a custom route with a constraint &lt;br&gt;&amp;nbsp;&lt;br&gt;routes.MapRoute("PostRoute", "blog/{id}", &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; new {controller = "Post", action = "Detail"}, &lt;br&gt;&amp;nbsp;&amp;nbsp; new {id = @"\d+"}); &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;The regular expression forces a match for requests like “/blog/509” but not &lt;br&gt;“/blog/foo.” I know, a lot of developers hate regular expressions, but the power in &lt;br&gt;front of you is nearly limitless. You can require that the path match any arbitrary &lt;br&gt;thing you can come up with! &lt;br&gt;&amp;nbsp;&lt;br&gt;As with everything else in ASP.NET MVC, constraints are also extensible. The &lt;br&gt;framework provides a simple interface called IRouteConstraint, and with it, &lt;br&gt;you can implement almost any kind of logic you can think of to accept or decline &lt;br&gt;the matching of a request. The basic scaffolding of a custom route constraint &lt;br&gt;class is shown in Listing 1.7. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.7: A bare bones custom route constraint class &lt;br&gt;&amp;nbsp;&lt;br&gt;public class SampleRouteConstraint : IRouteConstraint &lt;br&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp; public bool Match(HttpContextBase httpContext, &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Route route, &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string parameterName, &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RouteValueDictionary values, &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RouteDirection routeDirection) &lt;br&gt;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // do some logic &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true; &lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;There isn’t much to the interface, only a single method called Match(), which &lt;br&gt;returns a Boolean value indicating whether or not the request meets the constraint &lt;br&gt;(this one isn’t useful since it always returns true). You’ve got all of the relevant &lt;br&gt;data here to act on and make a decision on, including the HttpContext, so you &lt;br&gt;could make the matching criteria consider the identity or role of a user. You also &lt;br&gt;may consider some deeper format for the path that you can’t express (or would &lt;br&gt;rather not express) with a regular expression. &lt;br&gt;&amp;nbsp;&lt;br&gt;The order in which you create routes is the order in which MVC tries to find a &lt;br&gt;match, so the default route is placed last, unless you have some clever reason to &lt;br&gt;do otherwise. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;Master pages and partial views &lt;br&gt;&amp;nbsp;&lt;br&gt;By now you probably have a pretty good feel about how ASP.NET MVC works &lt;br&gt;compared to Webforms, but can’t imagine life without the beloved master page, &lt;br&gt;and probably user controls. Let’s review how those work. &lt;br&gt;&amp;nbsp;&lt;br&gt;A master page, or more to the point, a System.Web.UI.MasterPage, is a page &lt;br&gt;template from which other pages are derived from. In the Webforms world, it has &lt;br&gt;its own code behind and can conduct its own logic. It has most of the “outside” &lt;br&gt;HTML for the page, including the &amp;lt;head&amp;gt; and &amp;lt;html&amp;gt; tags, leaving &lt;br&gt;placeholders that will fill in the “inside” content from pages that use the master &lt;br&gt;page. These pages end with a .master extension. &lt;br&gt;&amp;nbsp;&lt;br&gt;User controls, typically files ending with .ascx, are a bit of an opposite from &lt;br&gt;master pages, as they’re fragments of markup and code that can dropped into a &lt;br&gt;page. Really old school developers might call them a robust version of server- &lt;br&gt;side includes, although these are much better since they can have awareness of &lt;br&gt;the page and can be loaded dynamically. &lt;br&gt;&amp;nbsp;&lt;br&gt;Master pages are still used in MVC, but in keeping with the separation of &lt;br&gt;concerns, they don’t do anything beyond simple display logic. If they need to &lt;br&gt;display data, the data should come from a controller. Let’s go back to our cat &lt;br&gt;names. Imagine that we intend to show those cat names on every page using our &lt;br&gt;master. Let’s work our way from the view backward, starting with the master &lt;br&gt;page itself in Listing 1.8. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.8: /Views/Shared/Site.master &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;%@ Master Language="C#" &lt;br&gt;Inherits="System.Web.Mvc.ViewMasterPage" %&amp;gt; &lt;br&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 &lt;br&gt;Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- &lt;br&gt;transitional.dtd"&amp;gt; &lt;br&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt; &lt;br&gt;&amp;lt;head runat="server"&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;title&amp;gt;Kittehs&amp;lt;/title&amp;gt; &lt;br&gt;&amp;lt;/head&amp;gt; &lt;br&gt;&amp;lt;body&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;div&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% foreach (var name in ViewData["AllKittehNames"] as &lt;br&gt;string[]) { %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;%= name %&amp;gt;&amp;lt;br /&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;% } %&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt; &lt;br&gt;&amp;nbsp; &amp;lt;/div&amp;gt; &lt;br&gt;&amp;nbsp;&amp;lt;asp:ContentPlaceHolder ID="MainContent" runat="server" /&amp;gt; &lt;br&gt;&amp;lt;/body&amp;gt; &lt;br&gt;&amp;lt;/html&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;If this reminds you of Listing 1.3, it should! The new and changed parts are in &lt;br&gt;bold. First, the directive was changed from Page to Master, and the Inherits &lt;br&gt;attribute changed to ViewMasterPage. Second, we’ve added a &lt;br&gt;ContentPlaceHolder, which is where pages using the master will put their &lt;br&gt;goodies. This part works just as it does in Webforms. &lt;br&gt;&amp;nbsp;&lt;br&gt;Next, let’s build a sample page that uses the master, as in Listing 1.9. Again, this &lt;br&gt;is fairly straight forward, and works similar to the way Webforms masters work. &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.9: Some random page using our master page &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;%@ Page Title="" Language="C#" &lt;br&gt;MasterPageFile="~/Views/Shared/Site.Master" &lt;br&gt;Inherits="System.Web.Mvc.ViewPage" %&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;asp:Content ID="Content1" &lt;br&gt;ContentPlaceHolderID="MainContent" runat="server"&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;p&amp;gt;Nothing exciting ever happens here.&amp;lt;/p&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;/asp:Content&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;If this page were our home page using the default routing (“/home/index”), the &lt;br&gt;view would be in “/Views/Home/Index.aspx” as expected. Countless other views &lt;br&gt;would also use this master, and those views would likely be called by any &lt;br&gt;number of controllers and their actions. The trick is that we need a centralized &lt;br&gt;place to populate view data the master can use, regardless of the view called &lt;br&gt;upon. Setting up this data in every controller action would suck, and be a waste &lt;br&gt;of time. &lt;br&gt;&amp;nbsp;&lt;br&gt;Fortunately, your object-oriented skills force you to write better code, and do it &lt;br&gt;once. You know that all controllers inherit from the Controller class, so it only &lt;br&gt;makes sense that you extend Controller yourself, and have all of your &lt;br&gt;controllers derive from that class instead. Listing 1.10 shows us our new &lt;br&gt;improved controller base and a skeleton of a home controller. &lt;br&gt;&amp;nbsp;&lt;br&gt;Listing 1.10: A controller base class and a controller that inherits from it &lt;br&gt;&amp;nbsp;&lt;br&gt;namespace HelloKittehs.Controllers &lt;br&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp; public abstract class ControllerBase : Controller &lt;br&gt;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ControllerBase() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ViewData["AllKittehNames"] = KittehNames.Get(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; public class HomeController : ControllerBase &lt;br&gt;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public ActionResult Index() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return View(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;br&gt;} &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;Now any controller that inherits from the ControllerBase will get the names &lt;br&gt;of the cats and place them in the ViewData collection, for use by the master &lt;br&gt;page. Note that we’ve marked the base class as abstract so it’s not accidentally &lt;br&gt;used as a controller directly. &lt;br&gt;&amp;nbsp;&lt;br&gt;The approximate analog to user controls are called partial views. They live in &lt;br&gt;.ascx files, and like master pages, they should not contain any real logic beyond &lt;br&gt;display tactics. Instead of registering them, as you would in Webforms, you drop &lt;br&gt;them in the page using a simple code block like this: &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;lt;% Html.RenderPartial("UserPartial"); %&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;This is from a case we’ll show you later, where we reuse components of a form &lt;br&gt;because they appear exactly the same in more than one view. For example, the &lt;br&gt;form fields used to add a user and edit a user are identical, so it’s easier to create &lt;br&gt;them once, and keep maintenance down to one place. The line of code above &lt;br&gt;follows the same conventions as the View() method of controllers, looking first &lt;br&gt;in the controller’s cousin folder (like “/Views/Admin”), and if it can’t find it &lt;br&gt;there, it’ll check the “/Views/Shared” folder. Since it’s a partial view, it’s looking &lt;br&gt;for UserPartial.ascx instead of an .aspx file. &lt;br&gt;&amp;nbsp;&lt;br&gt;Getting data to these partial views works the same way as it does for full views. &lt;br&gt;The data must be added to the ViewData in the controller action. You won’t &lt;br&gt;likely need to do it in some abstract base class, as you should know in most cases &lt;br&gt;that the partial view will be a part of the view you’re instantiating. As such, make &lt;br&gt;sure you get the partial view’s data set in the action method. &lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7078816" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/writing/default.aspx">writing</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC, front-to-back advantage revisited</title><link>http://weblogs.asp.net/jeff/archive/2009/05/05/asp-net-mvc-front-to-back-advantage-revisited.aspx</link><pubDate>Tue, 05 May 2009 18:32:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7074285</guid><dc:creator>Jeff</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7074285</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/05/asp-net-mvc-front-to-back-advantage-revisited.aspx#comments</comments><description>&lt;p&gt;A couple of month ago, &lt;a href="http://weblogs.asp.net/jeff/archive/2009/02/18/asp-net-mvc-the-front-to-back-advantage.aspx" target="_blank" mce_href="http://weblogs.asp.net/jeff/archive/2009/02/18/asp-net-mvc-the-front-to-back-advantage.aspx"&gt;I wrote a post about the surprising advantage of MVC as an enabling technology for front end folks&lt;/a&gt;. The more I work with it, the more I feel that it's the key advantage to using MVC. Given &lt;a href="http://weblogs.asp.net/jeff/archive/2009/03/11/seriously-the-ux-does-matter.aspx" target="_blank" mce_href="http://weblogs.asp.net/jeff/archive/2009/03/11/seriously-the-ux-does-matter.aspx"&gt;my rants on why I think the user experience is so important&lt;/a&gt;, you can understand why I'm so excited by this.&lt;/p&gt;&lt;p&gt;The shift in thinking to making more UX magic on the client end does throw us back into the world of Javascript, which is at least a little unfortunate since the debugging tools aren't nearly as good as the back end, but with jQuery and countless plugins easing the pain, I continue to be amazed at how quickly you can turn out really nice experiences. And even more surprising is that it doesn't require all kinds of drag-and-drop tools. You really get into the weeds with actual code, but without a ton of complexity.&lt;/p&gt;&lt;p&gt;What am I rambling about exactly? Your client code is often a couple of lines of code to make a JSON call or activate graphical notifiers, while your server code is often little more than a simple controller action. Even in the best encapsulation of script and rendering code in standard Web controls for Webforms, it's never that clean.&lt;/p&gt;&lt;p&gt;The question in my mind now is, what happens when ASP.NET v4 is out? With a much higher degree of rendering control, the "cleanliness" issues may be largely addressed when it comes to writing the client code. Will it still be as compelling?&lt;/p&gt;&lt;p&gt;I'm curious to hear stories from developers on how MVC is making their lives easier (or harder). &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7074285" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC: From Webforms to MVC - Introduction</title><link>http://weblogs.asp.net/jeff/archive/2009/04/30/asp-net-mvc-from-webforms-to-mvc-introduction.aspx</link><pubDate>Fri, 01 May 2009 02:42:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7069563</guid><dc:creator>Jeff</dc:creator><slash:comments>9</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7069563</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/04/30/asp-net-mvc-from-webforms-to-mvc-introduction.aspx#comments</comments><description>&lt;p&gt;This is the introduction for the book I've decided to write. I'm not at all sure how it will see paper yet, and I'm OK with that. I've been looking into self-publishing or on-demand publishing and I'm fairly convinced that it's a real option. I'm still willing to talk to "real" publishers as well, and as usual, I can be reached at jeff at popw dot com.&lt;/p&gt;&lt;p&gt;This is a very rough draft with very little copy editing or polish. I'm looking for feedback, though I understand there isn't a lot to give for an introduction. It does, hopefully, reveal the approach I'm going for. There will be more to come in the following weeks...&lt;br&gt;&lt;/p&gt;&lt;p&gt;The text is (c)2009, Jeff Putz, all rights reserved.&lt;/p&gt;&lt;p&gt;EDIT, See also:&lt;br&gt;&lt;a href="http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx" mce_href="http://weblogs.asp.net/jeff/archive/2009/05/08/asp-net-mvc-book-chapter-1-plumbing.aspx"&gt;Chapter 1 - The M, The V, The C and Plumbing&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Introduction&lt;br&gt;&lt;br&gt;Here we are, a new day, a new framework. The buzz on the Intertubes is loud with tales of ASP.NET MVC, a framework that adopts the model-view-controller pattern and applies it to Microsoft's Web application platform. It was built and developed almost completely in the open from late 2007 up until its final release at the Mix09 conference in Las Vegas.&lt;br&gt;&lt;br&gt;I was there, in fact, and attended the sessions. I watched Microsoftie Scott Hanselman entertain and inform the crowd by doing a demo without any slides, just code. There was much rejoicing among the attendees. And maybe some groaning too, about learning something new.&lt;br&gt;&lt;br&gt;What is MVC, and why bother?&lt;br&gt;&lt;br&gt;MVC stands for model-view-controller, a design pattern. If you believe Wikipedia (and who doesn't?), it was originally conceived in a paper about Smalltalk way back in 1979. The fundamental belief of this design pattern is that, in an object oriented sense, you can enforce a "separation of concerns" between the three components. This loose coupling allows you to swap out the pieces with less grief while increasing the ability to test your code. That's exactly how Scott Guthrie of Microsoft introduced it in November of 2007.&lt;br&gt;&lt;/p&gt;&lt;p&gt;--sidebar&lt;/p&gt;&lt;p&gt;It’s actually fun to see how the framework evolved, and Guthrie’s blog serves as a great history of this evolution. Here’s where it began:&lt;br&gt;http://weblogs.asp.net/scottgu/archive/2007/10/14/asp-net-mvc-framework.aspx&lt;br&gt;&lt;br&gt;--/sidebar&lt;br&gt;&lt;/p&gt;&lt;p&gt;The three "concerns" or components are:&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; Models: These are the components of your application that do the heavy lifting. You probably already have these in your Webforms projects, or any other kind of object-oriented development you do. They encapsulate logic, data access, rules and other entities. When done right, they should be able to execute in isolation, with no dependencies on a user interface. For example, if you have a class that logs some kind of security action by users, it shouldn't know or care about a Web site or its HttpContext. It would only need to know that some other entity will give it things like user names and IP addresses.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; Views: The user interface is a view, or the way that humans see and enter data. Just as models don't care about the Web, a view doesn't care one way or another about how your application logic will eat and process your credit card number or middle initial. It doesn't care where the "credit card declined" message came from either. It's like a slacker or atheist or something, never looking for deeper meaning.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; Controllers: These things are the ringleaders, the glue, the middlemen (or middlewomen) between the views and the models. They don't calculate tax or send e-mail or figure out how many forum posts you make per day. They choose what you should be seeing, and where to hand off the data that you've entered. They're kind of like project managers in that respect, in that they don't do anything other than coordinate the expectations between workers and managers.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br&gt;This is typically the point at which you'd see a diagram of the three entities in action, but we'll save a tree (and/or bits) and let you draw it yourself in the margin. It's not hard to visualize.&lt;br&gt;&lt;br&gt;I may be editorializing, but it could be argued that MVC has done a great deal to simplify and focus the development of Web applications. The pattern tends to get us thinking about specific tasks at hand, and a side effect of that is that it reduces the complexity of the user experience. When we see a URL with a path like "/chocolate/create," it's pretty easy to figure out what we as developers should be doing at that point.&lt;br&gt;&lt;br&gt;One of the big success stories of MVC as it pertains to the Web comes from Ruby on Rails, a framework released as open source back in 2004 by one of the founding partners of 37signals, a relatively small but successful company that builds Web-based applications. It was the first time I had heard of MVC (a computer scientist I am not), and I thought it was a very interesting way to quickly build stuff without all of the customary and boring stuff I had to do every day in ASP.NET. It wasn't quite compelling enough for me to jump in and learn Ruby. Still, it did capture my attention.&lt;br&gt;&lt;br&gt;--sideabar&lt;/p&gt;&lt;p&gt;People seem to have a love-them-or-hate-them feeling for 37signals, their “Signal v. Noise” blog and their e-book, Getting Real. I don’t agree with them all of the time, but I can’t ignore the fact that they tend to challenge a lot that we “know” about how business is supposed to be, especially in software development. I dig that.&lt;/p&gt;&lt;p&gt;--/sidebar&lt;br&gt;&lt;br&gt;Rails got a lot of traction not only with the 37signals products, but also sites like Twitter. One of the most impressive examples I've seen is Ravelry.com, a community site for knitters and those who love yarn. My wife visits every day. One developer builds and maintains that site while his wife manages the community. They have a Boston Terrier too, though I'm not sure what his role is.&lt;br&gt;&lt;br&gt;Why should you, the ASP.NET developer care that there's an MVC framework for your beloved platform? Because it may open some doors that make developing applications faster and require less work, especially when it comes to using AJAX and more client-side goodies.&lt;br&gt;&lt;br&gt;Good old ASP.NET proper comes with a lot of baggage. It was built to a large extent to simulate the statefulness of a traditional desktop application, making it easy to pick up for armies of VB6 programmers circa 2002. It comes with events and mechanisms that behind the scenes "remind" the server how the form looked before you did something to it, then it does some work and sends back the updated state of the page. It's a somewhat heavy process that produces a lot of markup in the HTML that you may not entirely need or want. It's powerful, but it might do a lot more than you need.&lt;br&gt;&lt;br&gt;The biggest win, in my experience, is the ease with which you can manipulate the DOM on a page and perform AJAX calls, thanks largely to jQuery, a popular and powerful open source Javascript library. You can do this today with Webforms, certainly, and the ASP.NET AJAX script library allows you to do it in a way that encapsulates the server and client code in a somewhat awkward harmony. But given the often weird markup that ASP.NET generates, it's not very straight forward.&lt;br&gt;&lt;/p&gt;&lt;p&gt;--sidebar&lt;/p&gt;&lt;p&gt;It should be noted that ASP.NET 4.0 will do a great deal to help you out here, by giving you better control over Viewstate and the client ID’s rendered in the HTML. Yes, that means you won’t have to figure out how you’re going to change the background color of ctrl00_ContentPlaceholder1_AddressBox_Message!&lt;/p&gt;&lt;p&gt;--/sidebar&lt;br&gt;&lt;br&gt;Does this mean that you should toss ASP.NET Webforms out? Absolutely not. Like anything, some tools work better for certain things.&lt;br&gt;&lt;br&gt;The pros and cons&lt;br&gt;&lt;br&gt;ASP.NET MVC has a lot going for it. If you're a control freak, you'll love that the HTML it produces is clean and exactly what you want. If you've used Webforms for any length of time, you know that sometimes the CSS you apply to something a WebControl generated yields completely strange or unintended results. Since the basic HTML helper methods generate simple markup, if you use them at all (they certainly save time), the end product is more of what you expect. Oh, and there's no Viewstate and scripts you can't explain.&lt;br&gt;&lt;br&gt;The ability to truly serve up different views based on device is remarkable. In the Webforms world, you'd need to rewrite the logic in your page's code-behind to bind the right data to the right controls, and likely do it differently for the mobile pages. In fact, you may have a totally different site for the mobile version, which is a lot of extra work.&lt;br&gt;&lt;br&gt;Because of the strict separation of concerns, you can test more code than ever. What we call the models here, your data entities and application logic, should be testable even in your current world. Now add in the controllers, which accept data and then pipe data back out. There has never been an easy and elegant way to test this logic in Webforms because of the overhead around the instantiation of a page object and its dependencies on HttpContext and its various properties like Request and Response.&lt;br&gt;&lt;br&gt;The URL routing system is particularly friendly for search engine optimization. Granted, this is a moving target for those who make a living trying to figure out how Google works, but even the common human can see a URL like "/news/cake" and instantly recognize it. The user that sees "/news.aspx?type=cake" may not. And hey, every win for link juice on Google can lead to more money in your pocket. Unless you're Twitter.&lt;br&gt;&lt;br&gt;Visual Studio's MVC tools have a lot of goodness baked right in. It will generate a strongly typed view based on one of your entities, and depending on your method of storing data, it may only require a few lines of code to save user input. So with a right-click and a few options, you're rocking out with a form that can display data, and create or update it. When used with LINQ to SQL or the Entity Framework, you're in business quickly.&lt;br&gt;&lt;br&gt;It's not all rainbows and puppies, of course, when it comes to ASP.NET MVC. First and foremost, it's a newer framework that fewer developers have worked with. Scarcity forces higher salaries. There's less experience out there because it simply hasn't been around as long as Webforms.&lt;br&gt;&lt;br&gt;It also lacks the kind of UX encapsulation that we get with Webforms. For example, I have a template based image gallery control that I've used time and time again. The markup that appears in each cell of the table can be anything I want, and I can tell it to leave a "hole" for a template containing an advertisement. All I do is drop it on a Webform and fill in the templates and it automagically works. This kind of reuse isn't in the strictest sense possible in MVC if you're adhering to the design pattern.&lt;br&gt;&lt;br&gt;The magic of having a simulated stateful form is also gone, with no postback mechanism. The entire event model is thrown out.&lt;br&gt;&lt;br&gt;Views feel a little like spaghetti code from old ASP (the scripting platform that used the tragic VBscript). Perhaps this one isn't entirely fair, because if you're really separating concerns into the model-view-controller pattern, what code you do have in the view should be little more than looping through a data collection or hiding something based on the data's value.&lt;br&gt;&lt;br&gt;And the biggest concern I have is that developers won't be thinking about security the way they should. It's already possible in Webforms (or any other platform) to put together an HTTP request with the right information to simulate a legitimate user action and do naughty things when the right preventative code is not in place, but it's even easier in MVC. A URL like "/cats/kill/42" is probably intended to execute cat.Lives-- and it's easy to guess. A lot more care has to be taken to validate data and enforce security.&lt;br&gt;&lt;br&gt;Stuff you need&lt;br&gt;&lt;br&gt;So now that you're convinced you want to use ASP.NET MVC, what do you need? The fine folks at Microsoft outline the details at http://asp.net/mvc, but here's a quick recap:&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; .NET Framework v3.5 with Service Pack 1&lt;/li&gt;&lt;li&gt; ASP.NET MVC v1.0 (this you'll have to download)&lt;/li&gt;&lt;li&gt; Visual Web Developer 2008 Express with SP 1 (another free download) or Visual Studio 2008 with SP 1 (not a free download).&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;Other tools that make your life easier:&lt;br&gt;&lt;br&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; Firebug, the Firefox plugin that gives you an inside peak to the goings on of the DOM and Javascript.&lt;/li&gt;&lt;li&gt; ReSharper plugin for Visual Studio by JetBrains. Seriously, in a world where you iterate and refactor every day, you need this.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;The sample app: CliqueSite.Blog&lt;br&gt;&lt;br&gt;Another blogging app? You bet. I chose to build a blogging app because it's a fairly simple thing to get your head around, and it includes the most common use cases for building Web applications.&lt;br&gt;&lt;br&gt;The application will likely evolve beyond the static version of this book, but we'll always keep the version that matches the book available for download. You can get it here:&lt;br&gt;&lt;br&gt;http://xxxxxxxxxxxx/&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;Who is this book for?&lt;br&gt;&lt;br&gt;This book makes some assumptions about what you already know. I will not subject you to all of the introductory concepts of ASP.NET, object oriented programming, HTML, CSS or Javascript. This book is for developers who have experience using Webforms and Microsoft's platform overall.&lt;br&gt;&lt;br&gt;Read on if you hope to learn the following:&lt;br&gt;&lt;br&gt;&lt;ul class="unIndentedList"&gt;&lt;li&gt; Learn the approximate analogs between Webforms and MVC, categorized by common use cases you already know.&lt;/li&gt;&lt;li&gt; What the model-view-controller pattern is, and how it's used in the ASP.NET MVC framework.&lt;/li&gt;&lt;li&gt; How to use an object relational mapping framework (ORM) in an ASP.NET MVC application. We'll be using LINQ to SQL.&lt;/li&gt;&lt;li&gt; Achieve better test coverage throughout your application.&lt;/li&gt;&lt;li&gt; Use AJAX and DOM manipulation to make a slicker, less chatty app.&lt;/li&gt;&lt;li&gt; Get down to the basics of sending bits to the browser.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;The book also assumes that you understand C#. This is not an indictment of VB.NET or anything, but rather a nod toward the larger audience. Even if you do use VB.NET, the syntax of C# should be easy enough to understand.&lt;br&gt;&lt;br&gt;Suggested approach to reading&lt;br&gt;&lt;br&gt;This book starts out with the bigger concepts used in ASP.NET MVC, then goes on to the specific use cases that you'll need to get things done. You may want to simply read cover to cover, but after the first chapter, feel free to skip around to satisfy your curiosity. You may have little use today for creating your own HTML helpers, but chances are that getting data in and out of a form is a top priority.&lt;br&gt;&lt;br&gt;For help and errata&lt;br&gt;&lt;br&gt;Visit http://xxxxxxxx/ for the latest information on this book. If you'd like to contact me, the author, drop a line to xxxxxxx.&lt;br&gt;&lt;br&gt;

&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7069563" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/writing/default.aspx">writing</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC book proposal</title><link>http://weblogs.asp.net/jeff/archive/2009/04/28/asp-net-mvc-book-proposal.aspx</link><pubDate>Tue, 28 Apr 2009 23:48:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7064141</guid><dc:creator>Jeff</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7064141</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/04/28/asp-net-mvc-book-proposal.aspx#comments</comments><description>&lt;p&gt;I'm not sure if it's the fact that I'm not working or what, but I'm very seriously thinking about writing another book. Not only that, but if I do it, it will be an ASP.NET MVC book. Some have suggested to me that the space is already too crowded.&lt;/p&gt;&lt;p&gt;In any case, my motivation is largely the same as it was for &lt;a href="http://www.amazon.com/exec/obidos/asin/0321294475/coasterbuzz-20" target="_blank" mce_href="http://www.amazon.com/exec/obidos/asin/0321294475/coasterbuzz-20"&gt;my first book&lt;/a&gt;, in that I'm dissatisfied with the way things are being presented today. I don't believe most books teach new things the way that developers want to learn. My approach is that there should be some basic understanding of concepts and intent when it comes to frameworks. It's hard to really distill that philosophy into a few sentences, but a part of it is that most developers seem to be always transitioning to something new, and they want to know how to apply the use cases they encounter every day to the new thing. That's the book I want to write.&lt;/p&gt;&lt;p&gt;As for the publication, I'm thining strongly about self-publishing. Here's the thing, no one ever gets rich from writing a programming book, and I certainly know that from experience. They open doors, but they don't make you rich. There are many ways to look at self-publishing. On one hand, you can sell one tenth of the number of books and make the same money (assuming on-demand printing and getting listed on Amazon), but the width of your audience may not be as large. Publishers also do things like layout and copy editing, although the overhead required is why authors make so little. As is the case for a lot of people in our field, I double majored in journalism and radio/TV, and the editing for my first book was relatively minor. I think I can wing it.&lt;/p&gt;&lt;p&gt;So aside from wondering what people think about yet another book, I intend to post a lot of it up front for feedback, if I choose to follow through. It's the kind of adventure I really would love right now, as I miss writing. Stay tuned!&lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7064141" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Career/default.aspx">Career</category><category domain="http://weblogs.asp.net/jeff/archive/tags/writing/default.aspx">writing</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>Paging links in ASP.NET MVC</title><link>http://weblogs.asp.net/jeff/archive/2009/03/29/paging-links-in-asp-net-mvc.aspx</link><pubDate>Sun, 29 Mar 2009 21:34:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7010074</guid><dc:creator>Jeff</dc:creator><slash:comments>7</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7010074</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/03/29/paging-links-in-asp-net-mvc.aspx#comments</comments><description>&lt;p&gt;Using the MVC framework on a real, if somewhat trivial, project has been a lot of fun so far. It's unlike me to wait for something to go final, but as I Google problems I encounter, it's good to see that I avoided a great many moving targets.&lt;/p&gt;

&lt;p&gt;That said, I've always enjoyed building custom controls. Sometimes they're not even necessary, but I find it to be a good exercise because generally there isn't anything hard that I haven't encountered before. I have a templated gallery control, for example, that populates a grid of images with an ad wrapped inside. With the community and photo stuff I often do, the potential for reuse is great.&lt;/p&gt;

&lt;p&gt;At first I worried that you lose some of this joy with MVC, but actually you just have to refactor it. One of the things I've had plenty of uses for is a paging control that builds a series of links to page data, based on the total number of pages and the index you're currently on. It's packaged up in POP Forums (&lt;a href="http://coasterbuzz.com/Forums/Topics/11.aspx" target="_blank" mce_href="http://coasterbuzz.com/Forums/Topics/11.aspx"&gt;see here&lt;/a&gt; for an example), and I've used it in a number of other places as well. Porting it to MVC was surprisingly easy by making it an HtmlHelper extension method.&lt;/p&gt;

&lt;p&gt;Here's what I ported into my simple blogging app...&lt;/p&gt;

&lt;code&gt;using System;&lt;br&gt;using System.Text;&lt;br&gt;using System.Web.Mvc;&lt;br&gt;using System.Web.Mvc.Html;&lt;br&gt;&lt;br&gt;namespace CliqueSite.Blog.Web.Helpers&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static class Html&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public static string PagerLinks(this HtmlHelper htmlHelper, string controllerName, string actionName, int pageCount, int pageIndex, string cssClass, string moreTextCssClass, string currentPageCssClass)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return PagerLinks(htmlHelper, controllerName, actionName, pageCount, pageIndex, "First", "Previous", "Next", "Last", "More", cssClass, moreTextCssClass, currentPageCssClass);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public static string PagerLinks(this HtmlHelper htmlHelper, string controllerName, string actionName, int pageCount, int pageIndex, string firstPage, string previousPage, string nextPage, string lastPage, string moreText, string cssClass, string moreTextCssClass, string currentPageCssClass)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; var builder = new StringBuilder();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (String.IsNullOrEmpty(controllerName) || String.IsNullOrEmpty(actionName))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Exception("controllerName and actionName must be specified for PageLinks.");&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (pageCount &amp;lt;= 1)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return String.Empty;&lt;br&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (String.IsNullOrEmpty(cssClass)) builder.Append("&amp;lt;div&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else builder.Append(String.Format("&amp;lt;div class=\"{0}\"&amp;gt;", cssClass));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (String.IsNullOrEmpty(moreTextCssClass)) builder.Append(moreText);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else builder.Append(String.Format("&amp;lt;span class=\"{0}\"&amp;gt;{1}&amp;lt;/span&amp;gt;", moreTextCssClass, moreText));&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (pageIndex != 1)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // first page link&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(htmlHelper.RouteLink("|&amp;lt;", new { controller = controllerName, action = actionName, id = 1 }, new {title = firstPage}));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (pageIndex &amp;gt; 2)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // previous page link&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int previousLink = pageIndex - 1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(htmlHelper.RouteLink("&amp;lt;&amp;lt;", new { controller = controllerName, action = actionName, id = previousLink }, new { title = previousPage }));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // calc low and high limits for numeric links&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int intLow = pageIndex - 1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int intHigh = pageIndex + 3;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (intLow &amp;lt; 1) intLow = 1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (intHigh &amp;gt; pageCount) intHigh = pageCount;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (intHigh - intLow &amp;lt; 5) while ((intHigh &amp;lt; intLow + 4) &amp;amp;&amp;amp; intHigh &amp;lt; pageCount) intHigh++;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (intHigh - intLow &amp;lt; 5) while ((intLow &amp;gt; intHigh - 4) &amp;amp;&amp;amp; intLow &amp;gt; 1) intLow--;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; for (int x = intLow; x &amp;lt; intHigh + 1; x++)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // numeric links&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (x == pageIndex)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (String.IsNullOrEmpty(currentPageCssClass))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(String.Format("{0} of {1}", x, pageCount));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else builder.Append(String.Format("&amp;lt;span class=\"{0}\"&amp;gt;{1} of {2}&amp;lt;/span&amp;gt;", currentPageCssClass, x, pageCount));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(htmlHelper.RouteLink(x.ToString(), new { controller = controllerName, action = actionName, id = x }));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (pageIndex != pageCount)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (pageIndex &amp;lt; pageCount - 1)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // next page link&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int nextLink = pageIndex + 1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(htmlHelper.RouteLink("&amp;gt;&amp;gt;", new { controller = controllerName, action = actionName, id = nextLink }, new { title = nextPage }));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // last page link&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append(htmlHelper.RouteLink("&amp;gt;|", new { controller = controllerName, action = actionName, id = pageCount }, new { title = lastPage }));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; builder.Append("&amp;lt;/div&amp;gt;");&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return builder.ToString();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;/code&gt;
&lt;p&gt;As I said, this is largely similar to the server control I built for the forum app, with two important differences. Instead of exposing properties for the control to determine the page numbers, text labels, CSS classes and such, they're set as parameters to the extension methods. The second difference is that the actual links aren't set up using the Control's ResolveUrl() method and some String.Format() methods. In fact, these are even easier, because you simply use the existing RouteLink() extension method to do all of the magic for you. They were even clever enough to give you endless flexibility to add attributes by way of anonymous types. Well done.&lt;/p&gt;
&lt;p&gt;In the controller, I'm setting up data like this:&lt;/p&gt;&lt;p&gt;
&lt;code&gt;public ActionResult Page(int page)&lt;br&gt;{&lt;br&gt;&amp;nbsp; int total;&lt;br&gt;&amp;nbsp; ViewData["Posts"] = _postRepository.GetPagedPosts(10, page, out total);&lt;br&gt;&amp;nbsp; ViewData["TotalPages"] = total;&lt;br&gt;&amp;nbsp; ViewData["Page"] = page;&lt;br&gt;...&lt;br&gt;&lt;/code&gt;
&lt;/p&gt;&lt;code&gt;&lt;/code&gt;&lt;p&gt;Then in the view itself, this is the magic that keeps it clean:&lt;/p&gt;
&lt;code&gt;&amp;lt;%= Html.PagerLinks("Admin", "Page", (int)ViewData["TotalPages"], (int)ViewData["Page"], &lt;br&gt;"theCssClass", "theMoreTextCssClass", "theCurrentPageCssClass") %&amp;gt;&lt;/code&gt;
&lt;p&gt;Does this violate any kind of design principles? I don't think so, but I'm rarely one to get extremely academic about this sort of thing. I'm not crazy about loading up views with a ton of display logic, and there would be a spaghetti mess to do something like this if you left it to the view all by itself.&lt;/p&gt;&lt;p&gt;What do you think? Can we expect to see an explosion of HtmlHelper extension libraries?&amp;nbsp;&lt;/p&gt;
&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7010074" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item><item><title>ASP.NET MVC: The front-to-back advantage</title><link>http://weblogs.asp.net/jeff/archive/2009/02/18/asp-net-mvc-the-front-to-back-advantage.aspx</link><pubDate>Thu, 19 Feb 2009 00:54:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6916392</guid><dc:creator>Jeff</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=6916392</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/02/18/asp-net-mvc-the-front-to-back-advantage.aspx#comments</comments><description>&lt;p&gt;In my current gig, I was surprised to find when I started that there were front-end and back-end developers. The front-side guys are mostly HTML, CSS and Javascript (actually, mostly jQuery) folks, while the back-end folks do all of the wiring up and heavy lifting on the server type stuff. Most of the places I've worked had developers touching everything, and if anyone was generating HTML, it was designers.&lt;/p&gt;&lt;p&gt;Then the big shocker came when one of the guys who was primarily front-end started messing around with the MVC framework. He already knew enough C# to be dangerous, but I was thrilled with his approach. He used MVC enough to get the various pages, er, views to show up correctly, and he was quickly cranking stuff out that any of the back-end guys could easily pick up.&lt;/p&gt;&lt;p&gt;I'm not sure why I didn't see it sooner, but in a shop where you have these divided responsiblities, the classic seperation of concerns translates well into real life! And probably the best part of it all is that it gets everyone excited about the framework, whlie leaving the client-side guys ready to do everything they know best, without getting in the way. It's a long way from messing with Web forms for them.&lt;/p&gt;&lt;p&gt;It's certainly going to be easy to do things incredibly wrong with MVC in terms of where you put code, but with a little coaching and a strong desire to push TDD, I can see a lot of potential for a new level of quality out of my team, even if some of them are a little green. I'm looking forward to the final release of MVC. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6916392" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category><category domain="http://weblogs.asp.net/jeff/archive/tags/MVC/default.aspx">MVC</category></item></channel></rss>