Scott Hanselman

The Weekly Source Code 43 - ASP.NET MVC and T4 and NerdDinner

June 25, '09 Comments [9] Posted in ASP.NET | ASP.NET MVC | NerdDinner | Source Code
Sponsored By

UPDATE: David's put the T4 template with some nice updates on CodePlex - It's the last download here.

I really advocate folks reading as much source as they can because you become a better writer by reading as much as writing. That's the whole point of the Weekly Source Code - reading code to be a better developer.

Reading code in Open Source projects is a good way to learn, especially if the project has been around a while and been successful, or if you already respect the team of people working on it. Less reliably, you can find snippets of code by searching and sharing code.

David Ebbo is scary clever. You know someone is smart when they come up with something you don't think you yourself could come up with on your own, but you still kick yourself for not thinking of it in the first place.

David's been experimenting with ways to make ASP.NET MVC better, specifically in the area of strongly-typed helpers. He's trying to get rid of strings, magic or otherwise.

He started with a BuildProvider (not a lot of folks know about BuildProviders...it's a powerful secret.) David didn't like these kinds of links in ASP.NET MVC:

<%= Html.ActionLink("Home", "Index", "Home")%>

So his BuildProvider would get added to the web.config:

<buildProviders>
<add extension=".actions" type="MvcActionLinkHelper.MvcActionLinkBuildProvider" />
</buildProviders>

And it would give you better methods like this, by poking around in your project and dynamically generating helper methods for you:

<%= Html.ActionLinkToHomeIndex("Home")%>

image_7Then, just days later, David got the T4 religion (I've argued we are doing a poor job of promoting this, so I'm telling everyone I know) and you should too. David explored the pros and cons of CodeDom vs. T4 for CodeGen then recreated his ASP.NET MVC Helpers using T4.

He's enabling not only better ActionLinks, but also Action Url Helpers, Constants for View Names, and a very clever thing, helpers for View Models, courtesy of David Fowler.

I thought this was particular cool, because it's a really visceral example of what you can do with Code Generation when you start exploring what you know at compile time with what you wish you knew at Design Time. It also reminds us that it's more than just Compile/Test/Run - your projects become more "meta" when you've got Inspect/CodeGen/Compile/Test/Run.

For example, if you have a ViewModel that say, has a string, rather than:

<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
<%= Html.ValidationMessage("Name", "*") %>

Why not this instead:

<%= ViewModel.Name.Label() %>
<%= ViewModel.Name.TextBox() %>
<%= ViewModel.Name.Validation() %>

This ViewModel stuff is in the earlier CodeDom templates, but not (yet?) the T4 templates. I hope we see it again, don't you? What do you think, Dear Reader, of this approach vs. the "Opinionated Input Builder" approach?

Just recently David put out his "new and improved" MVC T4 template, which is the one you should check out. I'm gently pushing him to put it on CodePlex somewhere and bake this goodness in. (Go tell him and Phil!)

He was doing some stuff at runtime, but has since moved to a pure design-time approach.

David Ebbo's ASP.NET MVC T4 Template applied to Nerd Dinner

image_d417b977-3d4c-4d3c-88e6-438b7a32582c David took the NerdDinner app as his base, and installed his T4 template. Just drop it in, and magic gets generated. How does it work, though?

Before he was using reflection but since this is a Design Time Code Generation process he had to delve into the *gasp* COM-based VS File Code Model API. However, this does really show off the power and flexibility of T4 itself. Kudos to David for looking COM in the face and not blinking!

Go download his template (direct ZIP here) and open it up.

T4 files can be scary to view in a text editor as they are constantly switching between <% code blocks %> and the code that's being generated. I use Notepad2 to view them, using the (interestingly enough) XML Document option that gives me rudimentary syntax highlighting.

If you're serious about T4, go get the Visual T4 Community Edition from Clarius for half-way syntax highlighting, or pay for the Professional Edition. They'll register themselves with Visual Studio and give you a better experience than just all-black text.

The first line in David's T4 template that made me chuckle was the the first line that's executed:

<# PrepareDataToRender(this); #>

As they say, it's funny because it's true. This is typical of code generation templates of all kinds. It's the "DoIt()" method that takes the left-hand's code (the COM crap) and prepares it for the right-hand (the template itself).

In order for David to make his template tidy and enable this nice clean level of abstraction:

<#  foreach (var controller in Controllers.Where(c=>c.IsPartialClass)) { #>
namespace <#= controller.Namespace #> {
public partial class <#= controller.ClassName #> {
protected RedirectToRouteResult RedirectToAction(ControllerActionCallInfo callInfo) {
return RedirectToAction(callInfo.Action, callInfo.Controller, callInfo.RouteValues);
}
...snip...

...he has to prepare an object model, and that's what PrepareDataToRender does. It's funny also because it's buried at the end, as it should be. It's really a pleasure to read and does a LOT considering it's only a 415 line template.

He has a number of cute gems like this one-line helper function that combines a little COM a little LINQ and a little magic to a list of methods to his template using the Visual Studio API. I laughed at the "functionFunction" enum. "Do you like her? or do you like her like her?"

// Return all the CodeFunction2 in the CodeElements collection
public static IEnumerable<CodeFunction2> GetMethods(CodeClass2 codeClass) {
// Only look at regular method (e.g. ignore things like contructors)
return codeClass.Members.OfType<CodeFunction2>()
.Where(f => f.FunctionKind == vsCMFunction.vsCMFunctionFunction);
}

David is clearly deep into this exploration right now, and is asking your opinion about the design. I would encourage you to flood him with feedback, or leave it hear and I'll tell him. ;)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

The HaaHa Show visits .NET Rocks

June 25, '09 Comments [14] Posted in Podcast
Sponsored By

3639851758_a6e4536564 While in Norway last week, Phil Haack and I were lucky enough to do a LIVE taping with Carl Franklin and Richard Campbell of .NET Rocks!

Hopefully the energy of the room is appropriately captured in the audio recording, because it was a hoot. The whole room was cracking up. It's always a joy to hang with Carl and Richard. Phil, less so. (Kidding!)

A small warning, though, this show may be entirely content-free, and while I can't exactly remember (We were all tired from presenting and likely hopped up on Pepsi Max) they may have been some coarse language.

You can listen to the show here. There's a number of formats available, including but not limited to, MP3 and WMA. If you want to subscribe, there's now a complete "master feed" of all .NET Rocks shows so you can get them all in one go if you like.

There's also a master feed of all Hanselminutes shows as well, by the way. Enjoy!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast 166 - Windows Presentation Foundation explained by Ian Griffiths

June 24, '09 Comments [7] Posted in Podcast | Windows Client | WPF
Sponsored By

51DF0boY5fL My one-hundred-and-sixty-sixth podcast is up. Scott chats with Ian Griffiths about Windows Presentation Foundation (WPF). Why is it so hard to master? What techniques should the WinForms developer learn first? Scott's working on a side project, and he and Ian brainstorm ways for Scott's application to use WPF more effectively.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is a sponsor for this show!

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET and Windows Forms. Enjoy the versatility of our new-generation Reporting Tool. Dive into our online community. Visit www.telerik.com.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

ASP.NET Ajax - Script Combining and moving ScriptResource.axd's to Static Scripts

June 24, '09 Comments [25] Posted in ASP.NET | ASP.NET Ajax | Javascript
Sponsored By

I've got a little something I'm doing and I wanted to take control over some scripts that were being added by ASP.NET WebForms. Remember that ASP.NET WebForms is designed around a control/component model, so you don't get 100% control over your markup. When you drag a control onto the page in WebForms, you expect it to work.

ScriptManager Basics

For example, if I'm going to do so stuff with GridView and an UpdatePanel, I might do this:













and this will cause some Web- and ScriptResources to be added to the generated HTML of my page, something like this:

 


Basically, ScriptResource.axd?d=blob&t=timestamp...these are JavaScript files that you don't need to deploy as they live inside the assemblies. They are managed by the ScriptManager tag/control in my source above.

Overriding ScriptResource and Hosting Static JavaScript Files

However, I might want to put them in static files and manage them myself. I can override their paths like this:







This will give me HTML like this:

 


NOTE: There're a few controls that don't use the ScriptManager, so they can't have their JavaScript suppressed. So far the Validators are the main culprits. I'm talking to the team and we'll see if we can't get that fixed in 4.0.

NEW IN 4.0: In 3.5 you also can't use the ScriptManager to suppress or set the path of WebResource.axd, but in 4.0 you will be able to by using ScriptReference. WebResource.axd is for non-Ajax scripts that use the Page.ClientScript.RegisterX APIs. It'll be nice to be able to use ScriptReference as the ScriptManager is smarter and gzip compresses as well.

In .NET 4.0 using the ScriptManager to suppress both ScriptResource and WebResource will allow you to get your pages down to a single script. We're looking also at a CDN (Content Distribution Network) option to get that static script hosted elsewhere as well. I'll show Script Combining in a second.

The name="" attribute has to line up with the name of the resource the script is stored in. I used Reflector to figure them out. There's a few like MicrosoftAjaxTimer.js, MicrosoftAjax.js, MicrosoftAjaxWebForms.js in System.Web.Extensions, and DetailsView.js, Focus.js, GridView.js, Menu.js, SmartNav.js, TreeView.js, WebForms.js, WebParts.js and WebUIValidation.js in System.Web.dll.

Remember, these ARE NOT ALL NEEDED. You only want these on an as-needed basis. When a control needs one, it'll ask for it. Just do a view-source on your resulting HTML and take control of the ones you want.

ScriptCombining in 3.5 SP1

Now, if I want to combine those 3 scripts into one, I can do this:









I've wrapped the scripts in a CompositeScript control and I get a single GZipped automatically combined script. I'll save that combined script away and host it at http://www.example.com/1.js statically. Now, I'll add the path attribute:









While not a direct feature of .NET 3.5, I'm able to greatly reduce the number of scripts and take control using a few simple techniques.

ScriptManager and CDNs in .NET 4.0

In .NET 4.0 we're trying to make this more formal and possibly get the page down to a single script that's hostable on a CDN. That will probably look something like this. Just enable CDN (Content Delivery Network) and all your ASP.NET Ajax scripts will come from a CDN that you can configure in global.asax once:

Pretty slick, and nicer than my hacks. For 4.0, the goal is for this to work with ScriptResource AND WebResource making your scripts quite tidy.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

These are the little bugs that lead to madness

June 23, '09 Comments [7] Posted in ASP.NET | IIS | Musings
Sponsored By

I received an interesting email today where a fellow was trying to make sure that all browsers could successfully download his company's MSI installer. He had found a blog post that I wrote SIX YEARS AGO on the Content-Disposition header and some trouble I'd had with Check Images. Just in case you're not clear, 6 years is like a century years on the internet.

Here's a little snippet from my incredibly old blog post:

HTTP Headers are name values pairs, so they are easily added with the Response object in ASP or ASP.NET You use it like this (the HTTP Headers):

HTTP/1.1 200 OK
<snip>
Content-Disposition: filename=checkimage.jpg
Content-Length: 76127
Content-Type: image/JPEG

Or, if you want to immediately prompt the user with a File Download Box:

HTTP/1.1 200 OK
<snip>
Content-Disposition: attachment; filename=checkimage.jpg
Content-Length: 76127
Content-Type: image/JPEG

However, Internet Explorer has never really got it right.

Here's a list of gotchas, starting with my own:

  • On IE 6.0, things mostly work, but if you ALSO setup Cache-Control: no-cache, your suggested filename (and type!) will be IGNORED.  A bummer if you have to choose between security and convienence.  Of course, security wins.
  • On IE 4, the attachment option is flaky, see Q182315
  • On IE 5.5, the attachment option is REALLY flaky, see Q267991 and Q279667 and Q281119
  • On IE 5.0, the filename suggested can mangle your filenames, see Q262042
  • On nearly all versions of IE, including 6.0, sometimes the browser will use the filename in the address bar instead of the Content-Disposition Header, and with IE5.5SP2 you're expected to change the UseCDFileName registry key, see Q303750.  This was fixed with IE6.0SP1.

IE's not the only browser with past trouble around this header, but it's been the worst historically. Last year, IE8 made a good move forward when it proposed (during the beta cycle) an "authoritative=true" addition to the Content-Type HTTP header. This would be a way for your server to basically insist that the Content-Type it offered was the correct one. Seems reasonable, like it should have always been that way, eh?

Here's an example on how we'd (under this OLD proposal) force an HTML page to be delivered and rendered as plaintext. Sam Ruby thought it was a good idea as well as sniffing, while inside the HTML5 spec, is generally considered a bad idea.

HTTP/1.1 200 OK
Content-Length: 108
Date: Thu, 26 Jun 2008 22:06:28 GMT
Content-Type: text/plain; authoritative=true;

<html>
<body bgcolor="#AA0000">
This page renders as HTML source code (text) in IE8.
</body>
</html>

Unfortunately this blog post was never updated. EricL (author of Fiddler and very nice person) wrote it, and he'll know I'm not picking on him personally, as this is a huge problem on all blogs, mine included. It's really hard to update old posts when they are obsolete. It's a manual process and all we as bloggers can do is our best to update our old posts with pointers to new information.

Two months later, this post came out and the final design that was agreed on with community feedback looked like this:

Over the past two months, we’ve received significant community feedback that using a new attribute on the Content-Type header would create a deployment headache for server operators. To that end, we have converted this option into a full-fledged HTTP response header.  Sending the new X-Content-Type-Options response header with the value nosniff will prevent Internet Explorer from MIME-sniffing a response away from the declared content-type.

For example, given the following HTTP-response:

HTTP/1.1 200 OK 
Content-Length: 108
Date: Thu, 26 Jun 2008 22:06:28 GMT
Content-Type: text/plain;
X-Content-Type-Options: nosniff

<html>
<body bgcolor="#AA0000">
This page renders as HTML source code (text) in IE8.
</body>
</html>

I'd like this post to serve as a reminder to all of us who are blogging technical content to update our posts if and when appropriate, and certainly when a reader points out errata. As the gent who emailed me so wisely put it:

"These are the little bugs that lead to madness."

Thoughts?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.