Wednesday, May 23, 2012

ASP.NET MVC Beta and RC Upgrades - Confirm your Expectations and Version Numbers in Production Environments

ASP.NET MVC Beta and RC Upgrades - Confirm your Expectations and Version Numbers in Production Environments:
I was working on an app locally using a daily build (newer than the currently released one) of ASP.NET MVC and deployed it to a host. I noticed this weird CSS issue. See how the text box on the left running on localhost is small and not styled while the one on the right running on the host is the correct width? You can click on the screenshot if you need to see more.
The Local website's textbox is poorly styled while the same app running the host has a correctly-styled textbox
I dug around a little and using the F12 browser developer tools (as well as good old View Source) I noticed the HTML being created was different! My local app was producing one bit of markup, then when I deployed the same app to my host I would get different markup!
Here's the app's markup running locally:
<input class="text-box single-line" data-val="true" 
data-val-date="The field EventDate must be a date." 
data-val-required="The EventDate field is required." 
id="EventDate" name="EventDate" type="datetime" value="5/29/2012 1:48:23 AM" />

Here's while running on the host:

<input class="text-box single-line" data-val="true" 
data-val-date="The field EventDate must be a date." 
data-val-required="The EventDate field is required." 
id="EventDate" name="EventDate" type="text" value="5/29/2012 1:48:23 AM" />

I realize I could have made it more obvious for this but I wanted to make the point that it took a second to notice that my standard ASP.NET MVC Helper call...

<div class="editor-field">
    @Html.EditorFor(model => model.EventDate)
    @Html.ValidationMessageFor(model => model.EventDate)
</div>

...was returning type="text" on the host but type="datetime" on my local machine and that difference was picked up CSS that targeted specific input tags. Weird. Now, EditorFor() can have its behavior overridden with custom files in ~\Shared\EditorTemplates so if there was a DateTime.cshtml in there that would make sense. There wasn't. I wasn't using the very lovely MvcHtml5Templates NuGet package by Scott Kirkland, so that wasn't it.

My spider-sense was telling me this must be a versioning issue but everything looked right. My only explanation was that somehow what was running on the host was different from what was running on my local machine.

I asked around work and Eilon Lipton suggested I run this snippet to check the version of ASP.NET MVC. He's basically saying "Hey, where did this well known type come from?"

public ActionResult ShowVersion()
{
    Type t = null;
    try
    {
        t = Type.GetType("System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35");
    }
    catch (Exception e)
    {
        Response.Write("Error finding MVC: " + e.ToString());
 
    }
    if (t == null)
    {
        Response.Write("Can't find MVC");
    }
    else
    {
        Response.Write("Found MVC at " + t.Assembly.CodeBase + ", IsInGac = " + t.Assembly.GlobalAssemblyCache + "<br>");
        var verAttr = t.Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyFileVersionAttribute), true)[0] as System.Reflection.AssemblyFileVersionAttribute;
        Response.Write("Version = " + verAttr.Version + "<br>");
 
    }
    return null;
}

Even better, rather than using this code, I can use the MvcDiagnostics NuGet Package and get THIS useful data from Brad Wilson. Hopefully he will update it for ASP.NET MVC 4 although it worked well.

MvcDiagnostics NuGet Package

This package gives you lots of useful information for debugging weird situations.

ASP.NET MVC Diagnostics Utility gives lots of useful data

Anyway, I put the diagnostic code in a controller and ran it and got this (this version is faked):

Found MVC at file:///C:/mysite/bin/System.Web.Mvc.DLL, IsInGac = False
Version = 4.0.77777.0

Hang on, but my local result was this:

Found MVC at file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Web.Mvc/v4.0_4.0.0.0__31bf3856ad364e35/System.Web.Mvc.dll, IsInGac = True
Version = 4.0.99999.0

My local version was newer and was running from the GAC (Global Assembly Cache) but the hosted version was older and running locally. That means I had older ASP.NET MVC files floating around that my project referenced and deployed to the host.

Aside: The specific change that "got" me here was actually the first external contribution accepted to ASP.NET MVC. It's the one that Miguel de Icaza sent a pull request for while I was on stage at DevConnections. The change is to output HTML5 input types for common default data types, like <input type="datetime"> for System.DateTime when using EditorFor().

It took me a moment to remember that I hadn't added the reference to ASP.NET MVC manually but rather via the NuGet Package Manager. This became crystal clear when I used the built-in NuGet Package Visualizer (from Tools | Library Package Manager) to see the package dependency chain:

The ASP.NET NuGet Packages as viewed in a directed graph in the NuGet Package Visualizer

Basically I needed to either update my out of date NuGet packages or add the assembly references manually without NuGet.

Since I was working on a daily build I did it manually. When you update your ASP.NET MVC 4 Beta applications to a newer build (like ASP.NET MVC 4 Release Candidate (whenever that comes out) or the final Release) you'll want to do update all your packages via NuGet and confirm you're getting the versions and behavior you expect. The NuGet Package Visualizer is a hidden gem indeed.

So, a general reminder to folks (and a specific lesson when upgrading between ASP.NET MVC builds):

  • know your version numbers, what you want, what you're referencing.
  • confirm your version numbers in your debug and production environment.
    • Perhaps a health-check page or a runnable Integration Test to assert your assumptions
  • update your NuGet packages keep your references, and thus your ~\bin up to date
  • know where your assemblies are being loaded from (the GAC or locally)

Hope this helps!



Sponsor: I want to thank my friends at DevExpress for sponsoring this week's feed. Take a moment and check out their stuff! Touch-enabled apps require developers to re-think design & user experiences. DevExpress tools help you take on these new challenges using your existing skills & today’s technologies.


© 2012 Scott Hanselman. All rights reserved.


DIGITAL JUICE

No comments:

Post a Comment

Thank's!