Jon Galloway

Server installation options for ASP.NET MVC 2

I’ve answered several questions about installing ASP.NET MVC 2 on a server lately, and since I didn’t find a full summary I figured it was time to write one up. Here’s a look at some of the top options:

  • WebPI
  • Bin deploy
  • Run the full AspNetMVC2_VS2008.exe installer
  • Command-line install with aspnetmvc2.msi

WebPI

WebPI has quickly become my favorite way to install Microsoft web platform software (including development tools) on my development machine, and it’s a great option for installing on the server as well. I like WebPI for a lot of reasons – here are the top three:

  • It’s a tiny download (less than 2 MB)
  • It figures out which dependencies you need and which you already have installed, so you get the smallest download and fastest install possible
  • It’s one place to go to get all the new releases

So if you have desktop access to the server, probably the best option is to install ASP.NET MVC 2 via WebPI.

Bin Deployment

ASP.NET MVC was designed so you can use it without needing install permissions, e.g. working with a hosting provider who didn’t have ASP.NET MVC installed. Phil Haack wrote up instructions for Bin Deploying an ASP.NET MVC 1.0 application, and it’s only gotten easier since then. If your server has ASP.NET 4 installed, you’ll just need to set the reference to System.Web.Mvc to “Copy Local”:

Bin Deploy for System.Web.MVC

If you’re running on .NET Framework 3.5 SP1, you’ll need to include System.ComponentModel.DataAnnotations.dll (by setting it to Copy Local as well):

  • System.ComponentModel.DataAnnotations.dll
  • System.Web.Mvc.dll

And if you don’t have .NET Framework 3.5 SP1, you’ll also need to include System.Web.Abstractions.dll and System.Web.Routing.dll, so the full list to Bin deploy ASP.NET MVC 2 on .NET Framework 3.5 without SP1 is:

  • System.ComponentModel.DataAnnotations.dll
  • System.Web.Abstractions.dll
  • System.Web.Mvc.dll
  • System.Web.Routing.dll

For a full walkthrough on this, see Phil’s blog post.

Run the full AspNetMVC2_VS2008.exe installer

The AspNetMVC2_VS2008.exe installer installer is available from Microsoft Downloads at the following (quite memorable) URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=c9ba1fe1-3ba8-439a-9e21-def90a8615a9

I’ll confess that I sometimes forget GUIDs, though, so I always just browse to http://asp.net/mvc and click on the Installer link near the top:

ASP.NET MVC Installer link

The installer is named AspNetMVC2_VS2008.exe, but it doesn’t require you to have Visual Studio 2008 (or any version of Visual Studio) installed, so it’s just fine to run it on a server. According to Jacques, who works on the installer: On a machine that doesn’t have VSTS/VWD installed, the EXE will autodetect this and only install the runtime component.

There are a few reasons you may not want to use this, though:

  • The installer shows a wizard that makes you click through a few steps. You might want to set up a silent / scripted install so you can include it as part of your server build process.
  • If the server happens to have VWD or VSTS installed (not a good idea usually, but it’s your server), the installer will also install the MVC tooling. You might not want that.

In those cases, you can do a scripted install with aspnetmvc2.msi.

Scripted install with aspnetmvc2.msi

There’s no direct download link for aspnetmvc2.msi; it’s included in AspNetMVC2_VS2008.exe. The good news is that AspNetMVC2_VS2008.exe is a self-extracting EXE, so you can either open it up with some archive utilities. Apparently it opens with WinRAR, but doesn’t open in either Windows Explorer, and 7-Zip just shows the resource information rather than the actual contents.

None of that’s a problem, though, because it’s a self-extracting exe. That means you can pass a –extract (or –x for short) argument at the command-line to extract it:

AspNetMVC2_VS2008.exe –x

You can also specify an extraction directory, like this:

Extracting AspNetMVC2_2008.exe 

Note: I don’t know of a way to specify the extraction directory in a way that prevents the confirmation prompt from being displayed. Do you?

There are three nested directories inside of the result: mvcruntime, mvctoolsvs2008, and mvctoolsvwd2008. The runtime, not surprisingly, is in the mvcruntime directory.

aspnetmvc2.msi hiding inside AspNetMVC2.exe

aspnetmvc2.msi can be run via command-line, and you can pass additional arguments supported by msiexec. Passing /q will specify a quiet install, so no dialogs are displayed. When you’re doing that, you’ll probably want to log the output in case something goes wrong, and you can do that using the /l argument:

C:\Users\Jon\Downloads\AspNetMVC2\mvcruntime>msiexec /i aspnetmvc2.msi /q /l*v .\mvc.log

You can find out more about Windows Installer (msiexec) command-line switches on MSDN, or you can just run msiexec /? to show the help.

You can drop the MVC_SERVER_INSTALL

The ASP.NET MVC 1.0 installer had an additional switch for server installation: MVC_SERVER_INSTALL. This switch is no longer required, because the installer detects if developer tools are installed and automatically falls back to the server install. So you’ll see people recommending that you use

msiexec /i AspNetMVC2.msi /l*v .\mvc.log MVC_SERVER_INSTALL="YES"

to install MVC 2, and while that extra flag won’t hurt anything, it’s completely unnecessary.

Installing on IIS 6

Note: I haven’t been near IIS 6 for a while now, so please let me know if I’m missing something.

Most of the difficulty in setting up ASP.NET MVC 1.0 to work on IIS 6 was in handling extension-less URL’s. Phil wrote up a walkthrough on setting up MVC 1.0 with ASP.NET 3.5 on IIS 6, and there’s a full tutorial on setting up ASP.NET MVC 1.0 with different versions of IIS over at http://asp.net/mvc.

Fortunately ASP.NET 4 takes care of that because it automatically handles extension-less URL’s. Thomas Marquardt has a great blog post explaining how it works under the hood.

Posted by Jon Galloway | 1 comment(s)
Filed under: ,

CodePlex now supports ClickOnce

I’m really excited to see that CodePlex just added support for ClickOnce!

I’ve worked a few open source client applications for Windows in both WPF and Winforms, and found that deployment – especially updates – was always the most painful part. It’s been a big drag on Witty deployment lately, so much so that I’d pretty much decided that any open source client applications I was going to work on in the future needed to be Silverlight Out-Of-Browser applications for the ease of deployment alone. Silverlight Out-Of-Browser is still a very interesting option for other reasons, like cross-platform support, but it’s nice to evaluate based the best end-user experience without having to make compromises based on deployment limitations.

ClickOnce is a great way to deploy small, open source applications:

  • It’s web-based
  • It handles auto-updates really smoothly
  • ClickOnce apps install per-user and don’t require administrator permission

The problem has always been that there was nowhere that offered free, simple ClickOnce hosting - free and simple both being two essential ingredients for open source development. If you wanted to support ClickOnce, you needed to set up and maintain two presences for your application – one to handle the open source project, and another to host the ClickOnce installation. That adds friction throughout the development process – for instance, when you add a new developer, they need FTP permissions on specific folders on the ClickOnce server as well as on the project hosting server. That kind of friction can really slow things down when you have very limited hours of everyone’s time.

CodePlex seemed like a natural fit for this, as a Microsoft hosted open source hosting solution. It’s no surprise that ClickOnce support was the number one requested feature on CodePlex.

All that to explain my excitement at seeing that CodePlex had officially added ClickOnce support today! Matt Hawley wrote up a blog post describing how to publish a ClickOnce release on CodePlex. I’ll take a look at setting that up for Witty and Data Dictionary Creator, and other open source apps I work on in the future.

In addition to the benefits to CodePlex users, I think this will be a boost to ClickOnce as a technology, for two reasons:

  • Increased use will (hopefully) more public information and more development, just like Visual Studio dogfooding did for WPF
  • Users will see ClickOnce and become familiar with it
  • That percentage of new ClickOnce users who also happen to be pointy haired bosses will ask for it

What do you think?

Posted by Jon Galloway | with no comments
Filed under: , ,

Using ViewModel information in an ASP.NET MVC 2 Editor or Display template

Editor and Display templates are a great new feature in ASP.NET MVC 2. They allow you to define a template which will be used for any datatype you’d like, and they can be set per-controller or site-wide.

The common scenario you may have seen is to set up a jQueryUI datapicker control for all DateTime editors site-wide. It’s really easy to set up – we create a /Views/Shared/DateTime.ascx file and add the following:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>

<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line" }) %>

<script type="text/javascript">
    $(function () {
        $("#<%: ViewData.TemplateInfo.GetFullHtmlFieldId(String.Empty) %>").datepicker();
    });
</script>

Then we add the references to our jQueryUI js & css in our Site.master (or on pages as appropriate) and magically all our DateTime fields get datepickers.

Templates for custom objects

That’s great, but one of the more powerful uses of MVC2 templating is to create display or editor templates for your own business objects. For example, if you have a site that sells disco balls, you could create a discoball.ascx display template and use it whenever you display a discoball object (and of course, the same for editing as well).

I demonstrated an example of that in the MVC Music Store – there’s an Editor template for an Album, and it’s used in both the Edit and Create views.

The problem: DropDowns require ViewModels

Many editor templates will require a ViewModel, since your model object won’t contain all the possible values for dropdowns. For example here’s the Edit template for an Album:

The Album table holds an ArtistId and a GenreId, which link to the Artist and Genre tables. That means that our Album model knows about an ArtistId, but doesn’t have a list of all the Artists necessary to display in the dropdown. To do that, we need to introduce a ViewModel, which contains all the information our View will need:

using System.Collections.Generic;
using MvcMusicStore.Models;

namespace MvcMusicStore.ViewModels
{
    public class StoreManagerViewModel
    {
        public Album Album { get; set; }
        public List<Artist> Artists { get; set; }
        public List<Genre> Genres { get; set; }
    }
}

Note: This is a simple usage of a ViewModel. There are several usage patterns for ViewModels, and more advanced ViewModel patterns dictate that you never pass your domain entities to your view. That’s a good topic for another post, but worth mentioning in passing here.

Our StoreManagerController.Create() action can then return a ViewModel which holds an empty Album object, along with lists of Artists and Genres to be populated:

// 
// GET: /StoreManager/Create

public ActionResult Create()
{
    var viewModel = new StoreManagerViewModel
    {
        Album = new Album(),
        Genres = storeDB.Genres.ToList(),
        Artists = storeDB.Artists.ToList()
    };

    return View(viewModel);
}

The Problem: How do we pass additional information to a Template?

Until the RTM release of MVC 2, the above scenario wouldn’t work. You just couldn’t get at the additional ViewModel information – in this case, the Album and Genre lists – from your template files. There was no way to pass the information from your view to your template, which meant that you had to resort to stuffing the additional information into ViewData:

// 
// GET: /StoreManager/Create

public ActionResult Create()
{
    var album = new Album();
    // NOTE: Example! Don't copy / paste / deploy! 
    // Not the best way to do this!
    ViewData["Genres"] = storeDB.Genres.ToList();
    ViewData["Artists"] = storeDB.Artists.ToList();

    return View(album);
}

But I avoid ViewData as much as possible. It’s a weakly typed collection, and I really dislike returning a strongly typed object to the View and then sneakily passing additional information via ViewData.

The Solution: Templated Helper overrides that pass additional View Data

MVC 2 RTM included a nice new feature to solve this exact problem. From the MVC 2 RTM release notes:

Templated Helpers Allow You to Specify Extra View Data

ASP.NET MVC 2 now includes new overloads of the EditorFor and DisplayFor methods. These overloads contain a parameter that accepts an anonymous object that can be used to provide extra view data. The view data provided in this parameter is merged with any existing view data that is passed to the template.

Let’s look at the implementation in MVC 2 Source Code in System.Web.Mvc.Html.TemplateHelpers.TemplateHelper():

if (additionalViewData != null) {
    foreach (KeyValuePair<string, object> kvp in new RouteValueDictionary(additionalViewData)) {
        viewData[kvp.Key] = kvp.Value;
    }
}

That’s pretty straightforward – the common method used by both EditorTemplates and DisplayTemplates checks for additional view data and copies it to ViewData keyed with the name we provided. So, yes, it’s still using ViewData, but at least our Controller and View don’t need to know that.

Putting it all together

The View (/Views/StoreManager/Create.aspx)

We’ll use one of the EditorFor() overrides which allows passing additional view data. Note that these overloads use anonymous objects as dictionaries. This pattern is pretty common throughout the ASP.NET MVC framework, since it allows for very terse key-value pair definitions.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcMusicStore.ViewModels.StoreManagerViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create Album
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>

    <% Html.EnableClientValidation(); %>

    <% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Create Album</legend>
        <%: Html.EditorFor(model => model.Album, new { Artists = Model.Artists, Genres = Model.Genres })%>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

    <% } %>

    <div>
        <%:Html.ActionLink("Back to Albums", "Index") %>
    </div>


</asp:Content>

The Editor Template (/Views/Shared/EditorTemplates/Album.ascx)

This editor template is strongly typed to the Album, but is making use of the additional view data for the Html.DropDownList() calls.

<%@ Import Namespace="MvcMusicStore"%>

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcMusicStore.Models.Album>" %>

<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>

<p>
    <%: Html.LabelFor(model => model.Title)%>
    <%: Html.TextBoxFor(model => model.Title)%>
    <%: Html.ValidationMessageFor(model => model.Title)%>
</p>
<p>
    <%: Html.LabelFor(model => model.Price)%>
    <%: Html.TextBoxFor(model => model.Price)%>
    <%: Html.ValidationMessageFor(model => model.Price)%>
</p>
<p>
    <%: Html.LabelFor(model => model.AlbumArtUrl)%>
    <%: Html.TextBoxFor(model => model.AlbumArtUrl)%>
    <%: Html.ValidationMessageFor(model => model.AlbumArtUrl)%>
</p>
<p>
    <%: Html.LabelFor(model => model.Artist)%>
    <%: Html.DropDownList("ArtistId", new SelectList(ViewData["Artists"] as IEnumerable, "ArtistId", "Name", Model.ArtistId))%>
</p>
<p>
    <%: Html.LabelFor(model => model.Genre)%>
    <%: Html.DropDownList("GenreId", new SelectList(ViewData["Genres"] as IEnumerable, "GenreId", "Name", Model.GenreId))%>
</p>

We can continue to use the ViewModel and Controller Action code I showed earlier (repeated here for clarity):

StoreManagerViewModel.cs:

using System.Collections.Generic;
using MvcMusicStore.Models;

namespace MvcMusicStore.ViewModels
{
    public class StoreManagerViewModel
    {
        public Album Album { get; set; }
        public List<Artist> Artists { get; set; }
        public List<Genre> Genres { get; set; }
    }
}

StoreManagerController.cs - Create():

// 
// GET: /StoreManager/Create

public ActionResult Create()
{
    var viewModel = new StoreManagerViewModel
    {
        Album = new Album(),
        Genres = storeDB.Genres.ToList(),
        Artists = storeDB.Artists.ToList()
    };

    return View(viewModel);
}
Posted by Jon Galloway | 21 comment(s)
Filed under: ,

Speaking at Umbraco Codegarden ‘10 on ASP.NET MVC 2

Next week I’ll be leading an ASP.NET MVC Bootcamp at the MVC pre-conference during annual Umbraco CodeGarden conference in Copenhagen starting next June 23rd.

Niels and Per have lined up a really impressive MVC Pre-conference. I’m really honored to be presenting alongside Steve Sanderson and Simone Chiaretta, two of the top MVC experts worldwide:

  • Simone will be talking about ASP.NET MVC Extensibility
  • Steve will be talking about Advanced ASP.NET MVC, BDD-TDD, and Web Security

I’ll be covering an introduction to ASP.NET MVC, pulling in some of my favorite bits from the Web Camps tour and going further with some pointers on where to go next with testing, migration strategies for components, what to learn next, etc. There are hands-on labs, as well, so if you’re coming make sure to be prepared and bring your laptop.

I’m really happy about the different tracks for this pre-conference – there will be great content for beginners and advanced developers going the whole time, so the talks can really focus on one experience level rather than trying to cater to the whole spectrum.

It looks like there are still some tickets available, so if you’re near Copenhagen next Wednesday and want to learn more about ASP.NET MVC, I can’t think of a better way to spend the day. (Codegarden 10 is sold out.)

On the way back, I’ll be stopping in to NYC for two days for the final Web Camp of the summer 2010 tour, speaking with Dan Wahlin. So if you’re too lazy to go to Copenhagen, maybe I’ll see you in NYC.

Posted by Jon Galloway | 2 comment(s)
Filed under:

Spending the summer at camp… Web Camp, that is

Microsoft is sponsoring a series of Web Camps this summer. They’re a series of free two day events being held worldwide, and I’m really excited about being taking part.

The camp is targeted at a broad range of developer background and experience. Content builds from 101 level introductory material to 200-300 level coverage, but we hit some advanced bits (e.g. MVC 2 features, jQuery templating, IIS 7 features, etc.) that advanced developers may not yet have seen. We start with a lap around ASP.NET & Web Forms, then move on to building and application with ASP.NET MVC 2, jQuery, and Entity Framework 4, and finally deploy to IIS. I got to spend some time working with James before the first Web Camp refining the content, and I think he’s packed about as much goodness into the time available as is scientifically possible. The content is really code focused – we start with File/New Project and spend the day building a real, working application.

The second day of the Web Camp provides attendees an opportunity to get hands on. There are two options:

  • Join a team and build an application of your choice
  • Work on a lab or tutorial

James Senior and I kicked off the fun with the first Web Camp in Toronto a few weeks ago. It was sold out, lots of fun, and by all accounts a great way to spend two days.

I’m really enthusiastic about the format. Rather than just listening to speakers and then forgetting everything in a few days, attendees actually build something of their choice. They get an opportunity to pitch projects they’re interested in, form teams, and build it – getting experience with “real world” problems, with all the help they need from experienced developers.

James got help on the second day practical part from the good folks that run Startup Weekend. Startup Weekend is a fantastic program that gathers developers together to build cool apps in a weekend, so their input on how to organize successful teams for weekend projects was invaluable. Nick Seguin joined us in Toronto, and in addition to making sure that everything flowed smoothly, he just added a lot of fun and excitement to the event, reminding us all about how much fun it is to come up with a cool idea and just build it.

In addition to the Toronto camp, I’ll be at the Mountain View, London, Munich, and New York camps over the next month. London is sold out, but the rest still have space available, so come join us!

Here’s the full list, with the ones I’ll be at bolded because - you know - it’s my blog. The the whole speaker list is great, including Scott Guthrie, Scott Hanselman, James Senior, Rachel Appel, Dan Wahlin, and Christian Wenz.

  • Toronto May 7-8 (James Senior and I were thrown out on our collective ears)
  • Moscow May 19
  • Beijing May 21-22
  • Shanghai May 24-25
  • Mountain View May 27-28 (I’m speaking with Rachel Appel)
  • Sydney May 28-29
  • Singapore June 04-05
  • London June 04-05 (I’m speaking with Christian Wenz – SOLD OUT)
  • Munich June 07-08 (I’m speaking with Christian Wenz)
  • Chicago June 11-12
  • Redmond, WA June 18-19
  • New York June 25-26 (I’m speaking with Dan Wahlin)

    Come say hi!

  • Posted by Jon Galloway | 6 comment(s)
    Filed under:

    Extending NerdDinner: Adding Geolocated Flair

    NerdDinner is a website with the audacious goal of “Organizing the world’s nerds and helping them eat in packs.” Because nerds aren’t likely to socialize with others unless a website tells them to do it.

    Scott Hanselman showed off a lot of the cool features we’ve added to NerdDinner lately during his popular talk at MIX10, Beyond File | New Company: From Cheesy Sample to Social Platform. Did you miss it? Go ahead and watch it, I’ll wait.

    Get Microsoft Silverlight

    One of the features we wanted to add was flair. You know about flair, right? It’s a way to let folks who like your site show it off in their own site. For example, here’s my StackOverflow flair:

    Great! So how could we add some of this flair stuff to NerdDinner?

    What do we want to show?

    If we’re going to encourage our users to give up a bit of their beautiful website to show off a bit of ours, we need to think about what they’ll want to show. For instance, my StackOverflow flair is all about me, not StackOverflow.

    So how will this apply to NerdDinner?

    Since NerdDinner is all about organizing local dinners, in order for the flair to be useful it needs to make sense for the person viewing the web page. If someone visits from Egypt visits my blog, they should see information about NerdDinners in Egypt. That’s geolocation – localizing site content based on where the browser’s sitting, and it makes sense for flair as well as entire websites.

    So we’ll set up a simple little callout that prompts them to host a dinner in their area:

    Nerd Dinner - Flair

    Hopefully our flair works and there is a dinner near your viewers, so they’ll see another view which lists upcoming dinners near them:

    NerdDinner - Flair - Dinners Near You

    The Geolocation Part

    Generally website geolocation is done by mapping the requestor’s IP address to a geographic area. It’s not an exact science, but I’ve always found it to be pretty accurate. There are (at least) three ways to handle it:

    • You pay somebody like MaxMind for a database (with regular updates) that sits on your server, and you use their API to do lookups. I used this on a pretty big project a few years ago and it worked well.
    • You use HTML 5 Geolocation API or Google Gears or some other browser based solution. I think those are cool (I use Google Gears a lot), but they’re both in flux right now and I don’t think either has a wide enough of an install base yet to rely on them. You might want to, but I’ve heard you do all kinds of crazy stuff, and sometimes it gets you in trouble. I don’t mean talk out of line, but we all laugh behind your back a bit. But, hey, it’s up to you. It’s your flair or whatever.
    • There are some free webservices out there that will take an IP address and give you location information. Easy, and works for everyone. That’s what we’re doing.

    I looked at a few different services and settled on IPInfoDB. It’s free, has a great API, and even returns JSON, which is handy for Javascript use.

    The IP query is pretty simple. We hit a URL like this: http://ipinfodb.com/ip_query.php?ip=74.125.45.100&timezone=false

    … and we get an XML response back like this…

    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <Ip>74.125.45.100</Ip>
      <Status>OK</Status>
      <CountryCode>US</CountryCode>
      <CountryName>United States</CountryName>
      <RegionCode>06</RegionCode>
      <RegionName>California</RegionName>
      <City>Mountain View</City>
      <ZipPostalCode>94043</ZipPostalCode>
      <Latitude>37.4192</Latitude>
      <Longitude>-122.057</Longitude>
    </Response>

    So we’ll build some data transfer classes to hold the location information, like this:

    public class LocationInfo
    {
        public string Country { get; set; }
        public string RegionName { get; set; }
        public string City { get; set; }
        public string ZipPostalCode { get; set; }
        public LatLong Position { get; set; }
    }
    
    public class LatLong
    {
        public float Lat { get; set; }
        public float Long { get; set; }
    }

    And now hitting the service is pretty simple:

    public static LocationInfo HostIpToPlaceName(string ip)
    {
        string url = "http://ipinfodb.com/ip_query.php?ip={0}&timezone=false";
        url = String.Format(url, ip);
    
        var result = XDocument.Load(url);
    
        var location = (from x in result.Descendants("Response")
                        select new LocationInfo
                        {
                            City = (string)x.Element("City"),
                            RegionName = (string)x.Element("RegionName"),
                            Country = (string)x.Element("CountryName"),
                            ZipPostalCode = (string)x.Element("CountryName"),
                            Position = new LatLong
                            {
                                Lat = (float)x.Element("Latitude"),
                                Long = (float)x.Element("Longitude")
                            }
                        }).First();
    
        return location;
    }

    Getting The User’s IP

    Okay, but first we need the end user’s IP, and you’d think it would be as simple as reading the value from HttpContext:

    HttpContext.Current.Request.UserHostAddress

    But you’d be wrong. Sorry. UserHostAddress just wraps HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"], but that doesn’t get you the IP for users behind a proxy. That’s in another header, “HTTP_X_FORWARDED_FOR". So you can either hit a wrapper and then check a header, or just check two headers. I went for uniformity:

    string SourceIP = string.IsNullOrEmpty(Request.ServerVariables["HTTP_X_FORWARDED_FOR"]) ?
        Request.ServerVariables["REMOTE_ADDR"] :
        Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

    We’re almost set to wrap this up, but first let’s talk about our views. Yes, views, because we’ll have two.

    Selecting the View

    We wanted to make it easy for people to include the flair in their sites, so we looked around at how other people were doing this. The StackOverflow folks have a pretty good flair system, which allows you to include the flair in your site as either an IFRAME reference or a Javascript include. We’ll do both.

    We have a ServicesController to handle use of the site information outside of NerdDinner.com, so this fits in pretty well there. We’ll be displaying the same information for both HTML and Javascript flair, so we can use one Flair controller action which will return a different view depending on the requested format.

    Here’s our general flow for our controller action:

    1. Get the user’s IP
    2. Translate it to a location
    3. Grab the top three upcoming dinners that are near that location
    4. Select the view based on the format (defaulted to “html”)
    5. Return a FlairViewModel which contains the list of dinners and the location information
    public ActionResult Flair(string format = "html")
    {
        string SourceIP = string.IsNullOrEmpty(
            Request.ServerVariables["HTTP_X_FORWARDED_FOR"]) ?
            Request.ServerVariables["REMOTE_ADDR"] :
            Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    
        var location = GeolocationService.HostIpToPlaceName(SourceIP);
        var dinners = dinnerRepository.
            FindByLocation(location.Position.Lat, location.Position.Long).
            OrderByDescending(p => p.EventDate).Take(3);
    
        // Select the view we'll return. 
        // Using a switch because we'll add in JSON and other formats later.
        string view;
        switch (format.ToLower())
        {
            case "javascript":
                view = "JavascriptFlair";
                break;
            default:
                view = "Flair";
                break;
        }
    
        return View(
            view,
            new FlairViewModel 
            {
                Dinners = dinners.ToList(),
                LocationName = string.IsNullOrEmpty(location.City) ? "you" :  
                    String.Format("{0}, {1}", location.City, location.RegionName)
            }
        );
    }

    Note: I’m not in love with the logic here, but it seems like overkill to extract the switch statement away when we’ll probably just have two or three views. What do you think?

    The HTML View

    The HTML version of the view is pretty simple – the only thing of any real interest here is the use of an extension method to truncate strings that are would cause the titles to wrap.

    public static string Truncate(this string s, int maxLength)
    {
        if (string.IsNullOrEmpty(s) || maxLength <= 0)
            return string.Empty;
        else if (s.Length > maxLength)
            return s.Substring(0, maxLength) + "...";
        else
            return s;
    }

     

    So here’s how the HTML view ends up looking:

    <%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<FlairViewModel>" %>
    <%@ Import Namespace="NerdDinner.Helpers" %>
    <%@ Import Namespace="NerdDinner.Models" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <title>Nerd Dinner</title>
            <link href="/Content/Flair.css" rel="stylesheet" type="text/css" />
        </head>
        <body>
            <div id="nd-wrapper">
                <h2 id="nd-header">NerdDinner.com</h2>
                <div id="nd-outer">
                    <% if (Model.Dinners.Count == 0) { %>
                    <div id="nd-bummer">
                        Looks like there's no Nerd Dinners near
                        <%:Model.LocationName %>
                        in the near future. Why not <a target="_blank" href="http://www.nerddinner.com/Dinners/Create">host one</a>?</div>
                    <% } else { %>
                    <h3>
                        Dinners Near You</h3>
                    <ul>
                        <% foreach (var item in Model.Dinners) { %>
                        <li>
                            <%: Html.ActionLink(String.Format("{0} with {1} on {2}", item.Title.Truncate(20), item.HostedBy, item.EventDate.ToShortDateString()), "Details", "Dinners", new { id = item.DinnerID }, new { target = "_blank" })%></li>
                        <% } %>
                    </ul>
                    <% } %>
                    <div id="nd-footer">
                        More dinners and fun at <a target="_blank" href="http://nrddnr.com">http://nrddnr.com</a></div>
                </div>
            </div>
        </body>
    </html>

    You’d include this in a page using an IFRAME, like this:

    <IFRAME height=230 marginHeight=0 src="http://nerddinner.com/services/flair" frameBorder=0 width=160 marginWidth=0 scrolling=no></IFRAME>

    The Javascript view

    The Javascript flair is written so you can include it in a webpage with a simple script include, like this:

    <script type="text/javascript" src="http://nerddinner.com/services/flair?format=javascript"></script>

    The goal of this view is very similar to the HTML embed view, with a few exceptions:

    • We’re creating a script element and adding it to the head of the document, which will then document.write out the content. Note that you have to consider if your users will actually have a <head> element in their documents, but for website flair use cases I think that’s a safe bet.
    • Since the content is being added to the existing page rather than shown in an IFRAME, all links need to be absolute. That means we can’t use Html.ActionLink, since it generates relative routes.
    • We need to escape everything since it’s being written out as strings.
    • We need to set the content type to application/x-javascript. The easiest way to do that is to use the <%@ Page ContentType%> directive.
    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.FlairViewModel>" ContentType="application/x-javascript" %>
    <%@ Import Namespace="NerdDinner.Helpers" %>
    <%@ Import Namespace="NerdDinner.Models" %>
    document.write('<script>var link = document.createElement(\"link\");link.href = \"http://nerddinner.com/content/Flair.css\";link.rel = \"stylesheet\";link.type = \"text/css\";var head = document.getElementsByTagName(\"head\")[0];head.appendChild(link);</script>');
    document.write('<div id=\"nd-wrapper\"><h2 id=\"nd-header\">NerdDinner.com</h2><div id=\"nd-outer\">');
    <% if (Model.Dinners.Count == 0) { %>
      document.write('<div id=\"nd-bummer\">Looks like there\'s no Nerd Dinners near <%:Model.LocationName %> in the near future. Why not <a target=\"_blank\" href=\"http://www.nerddinner.com/Dinners/Create\">host one</a>?</div>');
    <% } else { %>
    document.write('<h3>  Dinners Near You</h3><ul>');
      <% foreach (var item in Model.Dinners) { %>
    document.write('<li><a target=\"_blank\" href=\"http://nrddnr.com/<%: item.DinnerID %>\"><%: item.Title.Truncate(20) %> with <%: item.HostedBy %> on <%: item.EventDate.ToShortDateString() %></a></li>');
      <% } %>
    document.write('</ul>');
    <% } %>
    document.write('<div id=\"nd-footer\">  More dinners and fun at <a target=\"_blank\" href=\"http://nrddnr.com\">http://nrddnr.com</a></div></div></div>'); 

    Getting IP’s for Testing

    There are a variety of online services that will translate a location to an IP, which were handy for testing these out. I found http://www.itouchmap.com/latlong.html to be most useful, but I’m open to suggestions if you know of something better.

    Next steps

    I think the next step here is to minimize load – you know, in case people start actually using this flair. There are two places to think about – the NerdDinner.com servers, and the services we’re using for Geolocation.

    I usually think about caching as a first attack on server load, but that’s less helpful here since every user will have a different IP. Instead, I’d look at taking advantage of Asynchronous Controller Actions, a cool new feature in ASP.NET MVC 2. Async Actions let you call a potentially long-running webservice without tying up a thread on the server while waiting for the response. There’s some good info on that in the MSDN documentation, and Dino Esposito wrote a great article on Asynchronous ASP.NET Pages in the April 2010 issue of MSDN Magazine.

    But let’s think of the children, shall we? What about ipinfodb.com? Well, they don’t have specific daily limits, but they do throttle you if you put a lot of traffic on them. From their FAQ:

    We do not have a specific daily limit but queries that are at a rate faster than 2 per second will be put in "queue". If you stay below 2 queries/second everything will be normal. If you go over the limit, you will still get an answer for all queries but they will be slowed down to about 1 per second. This should not affect most users but for high volume websites, you can either use our IP database on your server or we can whitelist your IP for 5$/month (simply use the donate form and leave a comment with your server IP). Good programming practices such as not querying our API for all page views (you can store the data in a cookie or a database) will also help not reaching the limit.

    So the first step there is to save the geolocalization information in a time-limited cookie, which will allow us to look up the local dinners immediately without having to hit the geolocation service.

    Posted by Jon Galloway | 5 comment(s)
    Filed under:

    Introducing the MVC Music Store - MVC 2 Sample Application and Tutorial

    A couple weeks ago we did a soft release of a new ASP.NET MVC 2 Tutorial and Sample Application I’ve been working on over the past few months, the MVC Music Store. The source code and an 80 page tutorial are available on CodePlex. I’m also working on a video tutorial series for the ASP.NET website which will walk through building the application. After that, it’s time to talk about a feature length film and a worldwide MVC Music Store On Ice tour, but the plans aren’t completely set just yet.

    MVC Music Store

    MVC Music Store - Shopping Cart

    MVC Music Store - Admin

    Everything’s under friendly licenses - the source is available under Ms-PL license, and the tutorial is under Creative Commons Attribution license. The data for the application is based on the Chinook database, which is also under Ms-PL license.

    Who is this for?

    This tutorial is mostly written for beginners to ASP.NET MVC, including folks who are kind of new to web development. We already have a few tutorials out there at the intermediate to advanced developer level (Nerd Dinner, MVC Storefront) if you’re looking for something more advanced, but if you want to start with the basics of how ASP.NET MVC works before diving into repositories and IoC and TDD, this is a great place to get started.

    However, I think it’s also pretty useful if you’ve got some experience with ASP.NET MVC 1.0 but want to get a look at using some of the new features in ASP.NET MVC 2, especially model validation.

    What’s it cover?

    Store features:

    • Store browse
    • Shopping cart
    • Checkout
    • Membership and registration
    • Basic administration (add and edit albums)

    Tutorial covers:

    • ASP.NET MVC Basics
      • Controllers
      • Views
      • Models and ViewModels
    • Data access using Entity Framework 4
    • Model Validation
    • Editor and Display Templates
    • Use of ASP.NET Membership system
    • Client-side enhancements
      • Client-side validation
      • AJAX update showing both Ajax.ActionLink and jQuery

    How’s the code?

    I think this is a great tutorial sample, and I’m really proud of it. I spent a lot of time on it, and I had several reviews with Scott Guthrie and Scott Hanselman, as well as Phil Haack and Brad Wilson from the ASP.NET team. I’m hugely thankful to all of them for their input and patience, but of course I’ll take responsibility for any errors or killed kittens.

    This is a beginner tutorial, so the code focuses on communicating how ASP.NET MVC works. There are no dependencies, and things are written using a minimum of code and complexity. If you do see something that makes you angry or sad please let me know, but keep in mind that we’re not trying to build an enterprise store system here. As it is, the tutorial is 80 pages long, so I really tried to keep as much out as possible and focus on the basics.

    Where next?

    I’ve already gotten some good feedback on the discussion forum and at the Toronto Web Camp, where we used it as lab, so I’ll be updating the document and tutorial to get to a 1.0 release.

    I’m working on a video series for the ASP.NET website.

    I’ve also heard that people are interested in advanced, real-world scenarios, so that’s something I’ll be looking at next. Please let me know if you have any thoughts or requests there.

    Tailspin Spyworks Web Forms Sample

    If you haven’t seen it, check out Joe Stagner’s Tailspin Spyworks sample - an update of the popular iBuySpy application for ASP.NET 4 with all the new goodies.

    Visual Studio 2010 Zooming – Keyboard Commands, Global Zoom

    One of my favorite features in Visual Studio 2010 is zoom. It first caught my attention as a useful tool for screencasts and presentations, but after getting used to it I’m finding that it’s really useful when I’m developing – letting me zoom out to see the big picture, then zoom in to concentrate on a few lines of code.

    Zooming without the scroll wheel

    The common way you’ll see this feature demonstrated is with the mouse wheel – you hold down the control key and scroll up or down to change font size. However, I’m often using this on my laptop, which doesn’t have a mouse wheel.

    It turns out that there are other ways to control zooming in Visual Studio 2010.

    Keyboard commands

    You can use Control+Shift+Comma to zoom out and Control+Shift+Period to zoom in. I find it’s easier to remember these by the greater-than / less-than signs, so it’s really Control+> to zoom in and Control+< to zoom out.

    Like most Visual Studio commands, you can change those the keyboard buttons. In the tools menu, select Options / Keyboard, then either scroll down the list to the three View.Zoom commands or filter by typing View.Zoom into the “Show commands containing” textbox.

    Visual Studio 2010 - Zoom Commands

    The Zoom Dropdown

    If you forget the keyboard commands and you don’t have a scroll wheel, there’s a zoom menu in the text editor. I’m mostly pointing it out because I’ve been using Visual Studio 2010 for months and never noticed it until this week. It’s down in the lower left corner.

    Visual Studio - Zoom Menu

    Keeping Zoom In Sync Across All Tabs

    Zoom setting is per-tab, which is a problem if you’re cranking up your font sizes for a presentation. Fortunately there’s a great new Visual Studio Extension called Presentation Zoom. It’s a nice, simple extension that just does one thing – updates all your editor windows to keep the zoom setting in sync. It’s written by Chris Granger, a Visual Studio Program Manager, in case you’re worried about installing random extensions.

    Visual Studio 2010 - Presentation Zoom

    See it in action

    Of course, if you’ve got Visual Studio 2010 installed, you’ve hopefully already been zooming like mad as you read this. If not, you can watch a 2 minute video by the Visual Studio showing it off.

    Posted by Jon Galloway | 46 comment(s)
    Filed under:

    Visual Studio 2010 / .NET 4 / ASP.NET 4 / ASP.NET MVC 2 Launch

    Visual Studio 2010 and .NET 4 were released this morning. ASP.NET MVC 2 came out on March 11, but today’s a big deal for MVC as it’s now included “in the box” with Visual Studio.

    There’s a ton of great information out there on Visual Studio 2010 and ASP.NET 4. As always, Scott Hanselman has a great rollup post with what’s new. Visual Studio 2010 Beta and Release Candidate builds have been publicly available since May 2009, so while hopefully there are very few real surprises. I tried to pick out some things you may not have seen yet.

    Downloading and Installing

    Kick the tires with Visual Studio 2010 Express via WebPI

    Visual Studio 2010 Express is available via WebPI and is a quick, easy download. If you just want to kick the tires, WebPI is great because it just pulls down the bits you need vs. the ISO downloads which include the kitchen sink. As you can see below, the initial download is just 3.5 MB, and from there it just pulls down the minimal amount you need (vs. 2.2+ GB for the full Visual Studio 2010 Ultimate installer).

    Visual Web Developer 2010 Express - Web PI

    Do a custom install and just install what you’ll use

    This is just personal preference, but I recommend doing a custom install and just picking the features you know you’ll use. James Senior just posted the settings he usually uses, and I agree. I’ve been doing minimal installs for a while, and it’s great not just because it makes the initial install faster, but because it reduces the frequency and install time for future updates, hotfixes and service packs.

    If you do this, just keep the installation media (or ISO file) handy, and you can add on other features as you need them. Agile!

    Note: In previous versions of Visual Studio, I occasionally got myself into trouble by forgetting to install Team Developer and Tester Tools and then wondering why MVC Test Projects wouldn't load, but test support is included by default in the Visual Studio non-Express SKU’s.

    VS2010 - Small Install

    Side-by-side means no hard decisions

    • Visual Studio 2010 works fine alongside Visual Studio 2008
    • .NET 4 installs side-by-side with .NET 2 and doesn’t auto upgrade your old programs
    • ASP.NET MVC 1 and ASP.NET MVC 2 don’t conflict

    In general, you can install Visual Studio 2010 and .NET 4 without affecting your existing development and server environments, which makes both installation and gradual migration an easy decision.

    Disclaimers:

    • Windows Phone 7 dev tools don’t yet work with Visual Studio 2010 RTM. This problem will go away very soon.
    • Silverlight 4 dev tools don’t yet work with Visual Studio 2010 RTM. This problem will go away very soon.
    • While you can use Visual Studio 2008 and Visual Studio 2010 solutions and projects on the same machine and can use Visual Studio 2010 Multi-targeting to work with .NET 3.5 applications in Visual Studio 2010, converting a solution or project file to Visual Studio 2010 format is a one-way trip. That means that you can’t have one project file that can be opened by both Visual Studio 2008 and Visual Studio 2010, which can be a bit of a pain for team development scenarios where some team-members are really lame haven’t yet installed Visual Studio 2010. The best workaround there is to have separate project files, e.g. MyApplication.2008.csproj and MyApplication.2010.csproj.

    ASP.NET MVC 2 works with Visual Studio 2010 and Visual Studio 2008

    I’m assuming you’ve heard this, but ASP.NET MVC 2 is…

    What’s New Resources

    Rollup lists

    Feature specific

    Some Personal Favorite Features

    Summary

    Install Visual Studio 2010 right away! What are you still doing here?

    The new ASP.NET website

    ASP.NET - Home - NewWe launched a major refresh of the ASP.NET website today. It was really exciting to be a part of the update process, working with lots of very talented people including Scott Guthrie and Scott Hanselman. It’s a pretty major update, including:

    • New site-wide design
    • Redesigned Home page and Getting Started sections which streamline the experience for those who are new to ASP.NET
    • Revised and updated content areas for both ASP.NET Web Forms and MVC
    • Reviewed, re-categorized, and where appropriate, archived tutorial and video content. That was a pretty major undertaking, as some of that content had been around since ASP.NET 2.0

    As you’d expect, Scott Hanselman has a great overview of what’s changed site-wide, but I’d like to highlight a few areas I was most involved with – the MVC Content area and a major update to the Community Projects / Open Source page.

    ASP.NET/MVC changes

    The ASP.NET MVC section was the new kid on the block on the ASP.NET website, so the asp.net/mvc page had less of a visual change than many of the other top-level pages. A lot of the work was in streamlining and prioritizing content, so you can (hopefully) find what you want faster, and there are new video introductions like Scott’s How Best To Learn ASP.NET MVC.

    Here’s what it used to look like – content links on top, blog posts on the bottom, book links on the right.

    ASP.NET MVC - Old

    In addition to cleaning up and updating the top content, we merged in the top asp.net/mvc/learn content now, and moved the blog content to the community page. This should increase the content relevance and point you towards more video / tutorial content by topic.

    ASP.NET MVC - New

    We made substantial changes to asp.net/mvc/learn. The previous page hit you with a wall of text and links, and the sub-pages were more of the same. It was a lot of information, but it wasn’t really categorized or prioritized in a way that helped you find what you were looking for. I reviewed literally hundreds of pages of content (video and tutorial) to categorize, pick the top content to feature on top level pages, and find outdated content that should be archived.

    Here’s how the asp.net/mvc/learn page used to look:

    Learn ASP.NET MVC - Old

    Now that we’ve got the categories links on the asp.net/mvc page, they link to specific focus areas, like this asp.net/mvc/fundamentals page:

    ASP.NET - Fundamentals - New

    Keeping in mind that ASP.NET MVC 2 is due out soon, you should expect more changes here. For instance, I’ve been working hard on a new tutorial project with Scott Guthrie that will be finished soon, and I’ll be publishing a series of videos and tutorial articles covering that.

    The new Open Source / Community Projects Page

    ASP.NET - Open Source ProjectsHonestly, I’m most excited about one page in the site: the Open Source ASP.NET projects page. I got to run with this one, gathering input from a lot of open sourcey folks at Microsoft, compiling tons of links and project descriptions, and working with project owners when they didn’t have a published “Twitter pitch” description of their site published.

    This page is not beautiful – it’s a long list of links and text. We’ve got some future changes to the site that will help us with the presentation, but as it is now, I’ll admit that it’s not a beautiful webpage.

    And yet, it’s a thing of beauty. It made my day to see this kind of thing on Twitter: “Wow, an official Microsoft website listing open source tools for .NET. Didn’t think I’d see the day.” There’s a great open source community around ASP.NET open source, and it’s great to get the word out. This kind of thing is what the ASP.NET community is all about.

    Yes, this kind of effort is fraught with peril. There are tons of .NET open source projects out there, how do we manage the list, adding relevant projects and keeping them updated as they change names every week (oh, you silly open source folk, you)? I’m still figuring that out. I was warned this would be a challenge, and I accept it wholeheartedly, because it’s important. The alternative – not doing the page because it’s hard – is much worse.

    Let me know if I missed any that you think are important. I can’t add every project to the list, or it’ll grow large and useless, but I do want to make sure we capture a good sample.

    Note: I put a lot of work into this page, but it wouldn’t have been part of this release without Terri Morton’s help. She really saw the value in it and helped push it through when it really could have slipped from this release – thanks, Terri!

    Awesome? Awesomer.

    Is the site perfect? No, it’s not. But I hope you’ll agree that it’s a lot better. We’ve got a lot more planned for this year, and this is the first step.

    More Posts Next page »