<?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</title><link>http://weblogs.asp.net/jeff/default.aspx</link><description>The sillynonsense and .NET musings of Jeff Putz</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Silverlight 3 and Twitter</title><link>http://weblogs.asp.net/jeff/archive/2009/07/09/silverlight-3-and-twitter.aspx</link><pubDate>Thu, 09 Jul 2009 23:08:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7144042</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=7144042</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/07/09/silverlight-3-and-twitter.aspx#comments</comments><description>&lt;p&gt;The Twitterworld or (Twittersphere or whatever silly shit someone made up today) was all abuzz about the release of Silverlight 3 today, and I was shocked at how quickly it made the trends and how overwhelmingly it was positive.&lt;/p&gt;&lt;p&gt;I kind of knew it was coming already for various reasons (spelled NDA), but it makes me happy to see how excited most people are. I think that individually, the new features have been of average importance, but together, this is a really big deal release. My own pet excitement is attached to the out-of-browser feature, and to a lesser degree the H.264 support, but again, it's an overwhelmingly positive reaction.&lt;/p&gt;&lt;p&gt;But of course the haters are, well, haters. There's a lot of noise and hate on Twitter in general, which is why I fire up Tweetdeck once a week and then let it go for a week after that. The militant Adobe and anti-Microsoft camps started their douchebaggery before anyone ever installed the plugin (let alone the dev tools).&lt;/p&gt;&lt;p&gt;What bothers me the most is that it's a sad reflection on our profession. Platform/language zealotry is like a toxic substance that trivializes what we do. I've had a number of gigs where I happily let go of other projects based on Orcale, Java or whatever because they made sense for the situation. My job was to integrate and connect and ultimately make someone more money, and that's what mattered. If the platform met the requirements, I had to roll with it regardless of what I thought of the vendor or people doing the work. Why is that so hard for some people to deal with?&lt;/p&gt;&lt;p&gt;For the record, I'm pretty excited about Silverlight 3, and its new capabilities immediately bring some uses to mind. I recently deployed a small app to one of my sites for limited use, and install stats went from 25% to 35% in a few days. That's encouraging.&lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7144042" 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/Microsoft/default.aspx">Microsoft</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/Silverlight/default.aspx">Silverlight</category></item><item><title>304 Your images from a database</title><link>http://weblogs.asp.net/jeff/archive/2009/07/01/304-your-images-from-a-database.aspx</link><pubDate>Wed, 01 Jul 2009 04:58:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7137681</guid><dc:creator>Jeff</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7137681</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/07/01/304-your-images-from-a-database.aspx#comments</comments><description>&lt;p&gt;I was reading somewhere about some anecdotal evidence that Google doesn't like to index images that don't have some kind of modification time on them. When I relaunched &lt;a href="http://coasterbuzz.com/" target="_blank" mce_href="http://coasterbuzz.com/"&gt;CoasterBuzz&lt;/a&gt; last year, I moved all of my coaster pr0n to the database, and I've since noticed that none of the images are in fact indexed. Bummer.&lt;/p&gt;

&lt;p&gt;This also pointed out to me that I was doing something annoying. I was reading the data out every time for every image request. Not exactly the most efficient use of resources. Static files come down with information in the headers indicating when they were last modified (IIS, and presumably any other Web server does this), so the next time the browser makes the request, it the server compares the time in the request header with that of the file, and returns a 304 "not modified" response, and no file.&lt;/p&gt;

&lt;p&gt;That seemed like an obvious thing to do, even if it has no impact on Google indexing. Fortunately, it just required some refactoring of the IHttpHandler I had doing the work.&lt;/p&gt;

&lt;p&gt;Sidebar: This is probably the point at which some people will make a big stink about serving images out of a database, and how it's bad for performance or scalability. That's a fine argument to make, but outside of doing obviously stupid things, this is not an issue here. I'd prefer to address performance and scalability problems if I have them, not when I might have them, or never have them. Seriously, this is a site that does somewhere between a half-million and a million page views a month depending on the season. There are no performance issues here.&lt;/p&gt;

&lt;p&gt;So anyway, assuming for a moment that "photo" is a business object in this code, and it was determined by a query value to the handler, this is the meaty part of the ProcessRequest() method of the handler:&lt;/p&gt;
&lt;code&gt;if (!String.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"]))&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CultureInfo provider = CultureInfo.InvariantCulture;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lastMod = DateTime.ParseExact(context.Request.Headers["If-Modified-Since"], "r", provider);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (lastMod == photo.SubmitDate.AddMilliseconds(-photo.SubmitDate.Millisecond))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; context.Response.StatusCode = 304;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; context.Response.StatusDescription = "Not Modified";&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;br&gt;byte[] imageData = GetImageData(photo);&lt;br&gt;context.Response.OutputStream.Write(imageData, 0, imageData.Length);&lt;br&gt;context.Response.Cache.SetCacheability(HttpCacheability.Public);&lt;br&gt;var adjustedTime = DateTime.SpecifyKind(photo.SubmitDate, DateTimeKind.Utc);&lt;br&gt;context.Response.Cache.SetLastModified(adjustedTime);&lt;/code&gt;
&lt;p&gt;Yes, it probably needs to be refactored, and yes, it should probably be used in an IHttpAsyncHandler. But let's go through what's happening, starting at the bottom.&lt;/p&gt;
&lt;p&gt;The last few lines write out the actual bytes of the image (MIME type was set in previous code), then set the cacheability and the modification time of the image, which in my case is stored with the bits. The goofy part is where we create a new DateTime to make its kind known. If you don't explicitly state that it's a UTC time, the SetLastModified() method apparently adjusts it. I happen to store most times as UTC, so that was one less thing to worry about. This adds a header in the response called Last-Modified, and gives it a value that looks something like "Sun, 22 Jun 2003 16:27:19 GMT" (note that it truncates milliseconds and ticks, as you may expect).&lt;/p&gt;
&lt;p&gt;Now, on subsequent requests for the same image, the browser adds an If-Modified-Since header to the request, with the same date and time as the value. Here we're checking to see if the value is present on the request, and if so, let's see if we should do a 304. If it's there, we parse it into a DateTime and compare the time with the one stored in the business object. We're stripping off the milliseconds because the database will fill them in on our DateTime, and the incoming request doesn't have the same high resolution. If we have a match, we send out the 304 and return, not sending any more data or reading the bytes from the database.&lt;/p&gt;
&lt;p&gt;You can do this pretty easily in ASP.NET MVC as well.&lt;/p&gt;
&lt;code&gt;public ActionResult Image(int id)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;var image = _imageRepository.Get(id);&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (image == null)&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;throw new HttpException(404, "Image not found");&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (!String.IsNullOrEmpty(Request.Headers["If-Modified-Since"]))&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;CultureInfo provider = CultureInfo.InvariantCulture;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;var lastMod = DateTime.ParseExact(Request.Headers["If-Modified-Since"], "r", provider).ToLocalTime();&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (lastMod == image.TimeStamp.AddMilliseconds(-image.TimeStamp.Millisecond))&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;Response.StatusCode = 304;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Response.StatusDescription = "Not Modified";&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return Content(String.Empty);&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;&amp;nbsp;&amp;nbsp; &amp;nbsp;var stream = new MemoryStream(image.GetImage());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Response.Cache.SetCacheability(HttpCacheability.Public);&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Response.Cache.SetLastModified(image.TimeStamp);&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return File(stream, image.MimeType);&lt;br&gt;}&lt;/code&gt;
&lt;p&gt;Let me start by saying that this was something I just prototyped. It's in dire need of refactoring, as much of the logic isn't stuff you'd normally put in a controller action. I think there's a method for returning nothing on the Controller base, but I don't remember off the top of my head. If there is, you'd use that instead of Content() in the 304 case.&lt;/p&gt;&lt;p&gt;I hope this helps someone out! &lt;br&gt;&lt;/p&gt;
&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7137681" 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></item><item><title>Caught in a flesh storm, with a 90% chance of satisfaction</title><link>http://weblogs.asp.net/jeff/archive/2009/06/30/caught-in-a-flesh-storm-with-a-90-chance-of-satisfaction.aspx</link><pubDate>Tue, 30 Jun 2009 19:05:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7137358</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=7137358</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/30/caught-in-a-flesh-storm-with-a-90-chance-of-satisfaction.aspx#comments</comments><description>&lt;p&gt;This makes me laugh, in light of tech pundits who overstate the importance of Twitter.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;table style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245);" width="360" cellpadding="0" cellspacing="0" height="353"&gt;&lt;tbody&gt;&lt;tr style="background-color: rgb(229, 229, 229);" valign="middle"&gt;&lt;td style="padding: 2px 1px 0px 5px;"&gt;&lt;a href="http://www.colbertnation.com/" target="_blank" style="color: rgb(51, 51, 51); text-decoration: none; font-weight: bold;"&gt;The Colbert Report&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 2px 5px 0px; text-align: right; font-weight: bold;"&gt;Mon - Thurs 11:30pm / 10:30c&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 14px;" valign="middle"&gt;&lt;td style="padding: 2px 1px 0px 5px;" colspan="2"&gt;&lt;a href="http://www.colbertnation.com/the-colbert-report-videos/220019/june-29-2009/jeff-goldblum-will-be-missed" target="_blank" style="color: rgb(51, 51, 51); text-decoration: none; font-weight: bold;"&gt;Jeff Goldblum Will Be Missed&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 14px; background-color: rgb(53, 53, 53);" valign="middle"&gt;&lt;td colspan="2" style="padding: 2px 5px 0px; overflow: hidden; width: 360px; text-align: right;"&gt;&lt;a href="http://www.colbertnation.com/" target="_blank" style="color: rgb(150, 222, 255); text-decoration: none; font-weight: bold;"&gt;www.colbertnation.com&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="middle"&gt;&lt;td style="padding: 0px;" colspan="2"&gt;&lt;embed src="http://media.mtvnservices.com/mgid:cms:item:comedycentral.com:220019" style="display: block;" type="application/x-shockwave-flash" wmode="window" allowfullscreen="true" flashvars="autoPlay=false" allowscriptaccess="always" allownetworking="all" bgcolor="#000000" width="360" height="301"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 18px;" valign="middle"&gt;&lt;td style="padding: 0px;" colspan="2"&gt;&lt;table style="margin: 0px; text-align: center;" width="100%" cellpadding="0" cellspacing="0" height="100%"&gt;&lt;tbody&gt;&lt;tr valign="middle"&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.comedycentral.com/colbertreport/full-episodes" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Colbert Report Full Episodes&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.indecisionforever.com/" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Political Humor&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.colbertnation.com/video/tag/Jeff+Goldblum" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Jeff Goldblum&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;table style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245);" width="360" cellpadding="0" cellspacing="0" height="353"&gt;&lt;tbody&gt;&lt;tr style="background-color: rgb(229, 229, 229);" valign="middle"&gt;&lt;td style="padding: 2px 1px 0px 5px;"&gt;&lt;a href="http://www.colbertnation.com/" target="_blank" style="color: rgb(51, 51, 51); text-decoration: none; font-weight: bold;"&gt;The Colbert Report&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 2px 5px 0px; text-align: right; font-weight: bold;"&gt;Mon - Thurs 11:30pm / 10:30c&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 14px;" valign="middle"&gt;&lt;td style="padding: 2px 1px 0px 5px;" colspan="2"&gt;&lt;a href="http://www.colbertnation.com/the-colbert-report-videos/223487/april-02-2009/biz-stone" target="_blank" style="color: rgb(51, 51, 51); text-decoration: none; font-weight: bold;"&gt;Biz Stone&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 14px; background-color: rgb(53, 53, 53);" valign="middle"&gt;&lt;td colspan="2" style="padding: 2px 5px 0px; overflow: hidden; width: 360px; text-align: right;"&gt;&lt;a href="http://www.colbertnation.com/" target="_blank" style="color: rgb(150, 222, 255); text-decoration: none; font-weight: bold;"&gt;www.colbertnation.com&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr valign="middle"&gt;&lt;td style="padding: 0px;" colspan="2"&gt;&lt;embed src="http://media.mtvnservices.com/mgid:cms:item:comedycentral.com:223487" style="display: block;" type="application/x-shockwave-flash" wmode="window" allowfullscreen="true" flashvars="autoPlay=false" allowscriptaccess="always" allownetworking="all" bgcolor="#000000" width="360" height="301"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 18px;" valign="middle"&gt;&lt;td style="padding: 0px;" colspan="2"&gt;&lt;table style="margin: 0px; text-align: center;" width="100%" cellpadding="0" cellspacing="0" height="100%"&gt;&lt;tbody&gt;&lt;tr valign="middle"&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.comedycentral.com/colbertreport/full-episodes" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Colbert Report Full Episodes&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.indecisionforever.com/" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Political Humor&lt;/a&gt;&lt;/td&gt;&lt;td style="padding: 3px; width: 33%;"&gt;&lt;a href="http://www.colbertnation.com/video/tag/Jeff+Goldblum" target="_blank" style="font-family: arial; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(51, 51, 51); text-decoration: none;"&gt;Jeff Goldblum&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7137358" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/culture/default.aspx">culture</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Internet/default.aspx">Internet</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</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>Silverlight request: Make it work for iPhone apps</title><link>http://weblogs.asp.net/jeff/archive/2009/06/21/silverlight-request-make-it-work-for-iphone-apps.aspx</link><pubDate>Sun, 21 Jun 2009 21:38:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7131693</guid><dc:creator>Jeff</dc:creator><slash:comments>17</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/jeff/rsscomments.aspx?PostID=7131693</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/21/silverlight-request-make-it-work-for-iphone-apps.aspx#comments</comments><description>Obviously Silverlight runs on OS X. That much we know, since developers like me use it for non-development tasks instead of Windows. How difficult would it be to adapt it to stand-alone apps on the iPhone? Even if it had to include the runtime and base library (at a few megabytes), it would still be pretty cool, and we wouldn't have to use Xcode (which I'm not impressed with).&lt;br&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7131693" 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/Apple/default.aspx">Apple</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category></item><item><title>The joy of learning and surprise</title><link>http://weblogs.asp.net/jeff/archive/2009/06/10/the-joy-of-learning-and-surprise.aspx</link><pubDate>Wed, 10 Jun 2009 19:44:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7116575</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=7116575</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/10/the-joy-of-learning-and-surprise.aspx#comments</comments><description>&lt;div class="postBody"&gt;
			&lt;p&gt;I just read &lt;a href="http://www.wired.com/techbiz/people/magazine/17-05/mf_jjessay" target="_blank"&gt;the JJ Abrams essay&lt;/a&gt; in the previous issue of &lt;em&gt;Wired&lt;/em&gt;.
This essay really struck home about where we get joy out of life, and
how we seem so eager to overlook it. This quote sums it up for me:&lt;/p&gt;
&lt;blockquote&gt;"True understanding (or skill or effort) has become
bothersome—an unnecessary headache that impedes our ability to get on
with our lives (and most likely skip to something else). Earning the
endgame seems so yesterday, especially when we can know whatever we
need to know whenever we need to know it."&lt;/blockquote&gt;
&lt;p&gt;That is true on more levels than I can describe. While the Internet
has done so much to facilitate communication and bring people together,
it has also managed to trivilaize knowledge and the process of
acquiring it.&lt;/p&gt;
&lt;p&gt;I liken this to a suggestion that a friend made to me last year when
I was in the midst of writing code for one of my sites. He asked, "Why
don't you just use all of the free stuff out there and stictch it
together to make a site?" Aside from issues of integration, I asked
what I would get out of that as a developer. The "skill and effort" is
important to me. Deeper understanding is important to me. It's the same
reason someone may build a table instead of buying one from Ikea.
Indeed, sometimes the end product is less interesting than the journey
required to get there.&lt;/p&gt;
		&lt;/div&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7116575" width="1" height="1"&gt;</description><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/Community+News/default.aspx">Community News</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>Can you afford to blow off a part of your audience?</title><link>http://weblogs.asp.net/jeff/archive/2009/06/01/can-you-afford-to-blow-off-a-part-of-your-audience.aspx</link><pubDate>Tue, 02 Jun 2009 02:46:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7105536</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=7105536</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/06/01/can-you-afford-to-blow-off-a-part-of-your-audience.aspx#comments</comments><description>&lt;div class="blogitemtext"&gt;&lt;p&gt;The other day, me and Diana were talking
about the concept of "supported" browsers, and how big corporations
often restrict their online applications for use only with certain
browsers. This is a throwback to the days when you needed ActiveX or
certain IE-only features, and is rooted in old school corporate IT
nonsense.&lt;/p&gt;&lt;p&gt;But there are still a lot of financial institutions in
particular that keep telling you what browser to use, and that's just
stupid. I was looking at the stats for my sites today, and it shows
that 15% of visitors are Mac users, and only about 55% are IE users.
Even the iPhone is closing in on 1%, which is crazy.&lt;/p&gt;&lt;p&gt;That's the
world we live in. If you blow off 15% of your audience, can you afford
that? Imagine if Amazon did this. Their holiday quarter did $225
million in profit. Do you think they'd be OK with leaving $33 million
on the table?&lt;/p&gt;&lt;p&gt;I ran into another support issue today, when trying
to view the video clips for the Halo prequel and the body control stuff
for Xbox. I'd love to watch them, but they use some goofy stream that's
presumably Windows Media based and I can't watch them on the Mac.
Technologist apologists seem hell bent on declaring that this kind of
thing is OK, but especially for marketing intent, why would you exclude
any percentage of your audience?&lt;/p&gt;&lt;p&gt;I'm not suggesting we all need
to test for IE6 at this point, but come on man... with the standards
and frameworks we have, it isn't that hard to reach 99% of your
audience.&lt;/p&gt;&lt;/div&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7105536" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category></item><item><title>Twitter FAIL</title><link>http://weblogs.asp.net/jeff/archive/2009/05/27/twitter-fail.aspx</link><pubDate>Wed, 27 May 2009 19:14:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7098931</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=7098931</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/27/twitter-fail.aspx#comments</comments><description>&lt;p&gt;&lt;a href="http://weblogs.asp.net/blogs/jeff/twitterfail.png"&gt;&lt;img src="http://weblogs.asp.net/blogs/jeff/twitterfail.png" border="0"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;And productivity of nerds everywhere skyrocketed! :)&lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7098931" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category></item><item><title>Google Ad Manager: FTW!</title><link>http://weblogs.asp.net/jeff/archive/2009/05/19/google-ad-manager-ftw.aspx</link><pubDate>Wed, 20 May 2009 02:07:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7093220</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=7093220</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/19/google-ad-manager-ftw.aspx#comments</comments><description>&lt;p&gt;It doesn't seem like there are a lot of .NET developers out there who build their own stuff for their own sites, and if there are, they don't blog. But I know they're out there!&lt;/p&gt;&lt;p&gt;So if you have your own sites, and they're primarily a content endeavor, then you know that managing advertising is kind of a pain. There's the ad network chaining, and then things get crazy when all of a sudden you sell some campaigns on your own. Suddenly you need some way to deliver those ads, and the options have been to either roll your own solution, rent some service or buy software.&lt;/p&gt;&lt;p&gt;All of those solutions have a down side, especially in terms of cost. I first built my own solution circa 2005, and even put out a very rough beta of it. That's as far as it went. It didn't have any significant reporting, but it did deliver ads pretty well with frequency capping and what not. It wasn't a retail-ready product, but it was serving me well enough.&lt;/p&gt;&lt;p&gt;But it couldn't geo-target, do really sweet reporting and it was never tested beyond a handful of campaigns at a time. I looked into buying a third-party product, or using services, but they were all way too expensive for the volume I was doing. So my solution has been serving since then, mostly mixing remnant ads from various networks.&lt;/p&gt;&lt;p&gt;Along comes Google and they start offering &lt;a href="https://www.google.com/admanager/" target="_blank" mce_href="https://www.google.com/admanager/"&gt;Google Ad Manager&lt;/a&gt;. I've used some of the online services before, which vary from usable (the old old pre-Google DoubleClick) to downright horrible (Atlas), as well as some of the purchased products (most expensive of which was Open AdStream, also purchased back in the day). I've also used the publisher default management of everyone from Federated Media (awesome) to Burst and Casale (decent) to Tribal Fusion and ValueClick (sub-optimal). I've seen more ad platforms than any one person should, and the only one that did what I really wanted was my own, if you don't count the lack of reporting.&lt;/p&gt;&lt;p&gt;"GAM," as they call it in the video and WebEx, makes sense. From the set up perspective, it has "slots" that are the physical postion of ads, and placements, which in turn can be any combination of slots. It's a many-to-many relationship. So you can group various slots together to form units that correspond to sites, sections or whatever. These can be further combined into products, which are optional, but an easy way to package a frequent or common buy. The UI around these might be a little confusing at first, but once watch their video clips, it makes sense.&lt;/p&gt;&lt;p&gt;To put ads into these slots, you create an order. An order basically is just a name and flight dates. Order have line items, which indicate a delivery priority (more on that in a moment), the cost type (CPM, CPC, CPD), targeting, frequency caps, and most importantly, the placements where the ads will go. At that point, you can upload Flash or image creative, third-party HTML/script, etc. It will match creative sizes to the available placement sizes.&lt;/p&gt;&lt;p&gt;Once you get that cascade of entities, it's actually really smooth sailing. The big strength comes in the fact that the delivery priorities are so flexible. At the top is exclusive delivery, which takes precedent regardless of cost unit. Premium and standard are the regular serving of campaigns, smoothed out in delivery frequency based on quantity, caps, targeting, etc., only premium trumps standard in priority. Remnant and house priority are, as the name implies, delivered when the others have no ad to serve, and your ad networks fall into the remnant category.&lt;/p&gt;&lt;p&gt;So what does Google get out of this? Well obviously they want it to be easy for you to run AdSense ads, whenever it makes sense. If you've ever just plugged AdSense tags into a site template and let it serve, you know the payout can be miserable, depending on your content. However, there is some optimized magic here. You can enable AdSense by slot. Then you give your remnant ads a "value CPM," which is like a virtual CPM for AdSense to compete with. For example, say I set up a Tribal Fusion order as remnant ads, and I set the line item for my 300x250 placements at $1.00. Now when it comes time for an ad to be served, Google determines if it can serve AdSense ads that, over time, would yield a higher CPM than $1.00. If it can, then it serves an AdSense ad, otherwise, it goes with the remnant campaign, in this case the Tribal Fusion ad. The result is that it only shows AdSense on a particular page if it knows that it can score more cash than the virtual CPM you specified.&lt;/p&gt;&lt;p&gt;I did a week-long test of this on one ad position, and once it had some time to serve tens of thousands of ads, it turned out that the AdSense CPM was in fact higher. But there is less waste because on the pages that wouldn't generate very good AdSense ads, it allows the remnant (ad network) stuff to show through, which for me is generally CPM and not CPC like AdWords. It surprisingly works really well!&lt;/p&gt;&lt;p&gt;The only issue I've encountered, and really it's not a deal breaker, is that I can't set weights for different remnant orders or line items. You can only set manual weighting among creatives in a specific line item, which is something you'd probably not do in a regular campaign since it optimizes to show more of the creative that has a stronger click-through. For an independent publisher, this is not likely a big deal, since you probably have a chain of ad providers that show as default to one another, based on their ability to pay out.&lt;/p&gt;&lt;p&gt;Overall it's a well thought out product, and it makes me want to sell more of my own ads! Again, I understand that Google's intention is to get you running more AdSense, and they win the business because it removes the biggest objection I always had: Too much waste that diluted the overall CPM of any given page on my site. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7093220" 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></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>Job recruiting turned to spamming, staffing firms marginalizing themselves</title><link>http://weblogs.asp.net/jeff/archive/2009/05/04/job-recruiting-turned-to-spamming-staffing-firms-marginalizing-themselves.aspx</link><pubDate>Mon, 04 May 2009 21:13:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7072321</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=7072321</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/04/job-recruiting-turned-to-spamming-staffing-firms-marginalizing-themselves.aspx#comments</comments><description>&lt;p&gt;The poor economy has had an interesting (and unfortunate for people in my position) side effect for the business of matching employers and people. The staffing agencies themselves have become so desperate to fill so few jobs that they've gone to the point of spamming prospects with irrelevant "leads." It's literally the same approach that spammers take, feeling that e-mail is cheap, and send out enough and something will stick.&lt;/p&gt;&lt;p&gt;Some of it goes as far as to insult you. Next to the unsubscribe message, one that I got today said, "You opted to receive this e-mail because you posted your resume on a job board." Seriously? So I could be spammed by someone plugging a job in Portland that I'm not qualified for?&lt;/p&gt;&lt;p&gt;Even worse, when someone posts a job on Monster, all of the recruiters call you that morning pitching the same thing, which by then if it was a fit I already applied for. I keep wondering what value these staffing firms add for anyone. In the Internet age they strike me as unnecessary. &lt;br&gt;&lt;/p&gt;&lt;p&gt;The big difference in looking for a job now compared to a year ago is that you the candidate have to do things the old fashioned way, by looking for positions yourself and firing up the network. The network of course says, "Ain't got nothin' fo' ya man."&lt;/p&gt;&lt;p&gt;It's a bad scene out there, especially in the Cleveland/Akron market. The recruiters I trust say they see a slight up tick in other markets they work in, but not here. I'm perfectly willing to move, but not for contract work, and not without some measure of relocation assistance. We just got married and have two mortgages, so moves initiated by us... not likely for about the next year.&lt;/p&gt;&lt;p&gt;It's a good time to reinvest in yourself, volunteer and write something on your own. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7072321" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/Career/default.aspx">Career</category><category domain="http://weblogs.asp.net/jeff/archive/tags/Community+News/default.aspx">Community News</category></item><item><title>Empower for ISV's: FAIL</title><link>http://weblogs.asp.net/jeff/archive/2009/05/04/empower-for-isv-s-fail.aspx</link><pubDate>Mon, 04 May 2009 15:45:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7071907</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=7071907</wfw:commentRss><comments>http://weblogs.asp.net/jeff/archive/2009/05/04/empower-for-isv-s-fail.aspx#comments</comments><description>&lt;p&gt;I was just thinking back to Mix when I remembered some mention of the ISV program, where a couple hundred bucks a year gets you MSDN and such. The catch is that you have to release some kind "packageable" software. Seriously? Does this make sense from the company moving all of its stuff to Web-based solutions and driving the developer community in the same direction.&lt;/p&gt;&lt;p&gt;Not very well thought out, actually. &lt;br&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=7071907" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/jeff/archive/tags/Microsoft/default.aspx">Microsoft</category><category domain="http://weblogs.asp.net/jeff/archive/tags/General+Software+Development/default.aspx">General Software Development</category></item></channel></rss>