Tuesday, February 12, 2008

This is one of those entries that attempts to fill a void in online search for a particular topic.  I ramble on for a while to give enough context so that a search engine can match it up in a relevant manner.

I was debugging something the other day, and thought I had come across a heinous bug in the CLR.  Turns out, everything was working fine and the bug was in the app.

A program was crashing and it was a managed exception, so I attached to it with VS2008 and dug in.  The first thing I noted was that this was a retail build, so my stack was collapsed in a number of places... expected, keep moving.  Then, I noted an interesting frame on my stack (this is a contrived example, not the actual thing I saw):

>    CanonTest.exe!CanonTest.GenericType<System.__Canon,int>.SomeOperation() + 0x55 bytes   

That's weird, what is System.__Canon and what's it doing here? Surely this must be a horrible CLR bug where my method tables are being corrupted!  Internet searches seem to be confirming my thoughts.  A few others with random __Canon's showing up on the stack do look like bugs.  After a bit more reasoning, I come to the conclusion that System.__Canon must be special in some way since it follows the pattern for such things...  marked internal, pre-pended with double-underscore.

A few emails later, I had my answer.  System.__Canon is the special type that is used to identify "canonical" generic type instantiations. It typically only shows up in release builds, so you don't normally see it on the stack while debugging.  So, it's easy to assume something's wrong when you do see it.

If you'll recall, one of the really cool things about the Generics implementation is that it allows for code sharing.  Jitted methods can be shared between compatible type instantiations.  For instance, the code for SomeGenericType<string> can likely be shared with SomeGenericType<Foo> (where Foo is another reference type. We don't currently share code between value types, so you'll continue to see those on the stack such as in the example).  That shared code needs to live somewhere, so we have the concept of a "canonical" generic type instantiation that is identified by __Canon as the type parameter.

Also, in alot of debugger stack representations you'll get the GenericType`2 form rather than expanded form.  In that case, you never see __Canon.

For some more info on Generics and code sharing, see Joel Pobar's excellent blog entry on the subject.

posted on Tuesday, February 12, 2008 11:56:35 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Wednesday, January 09, 2008

A twist on Bill Cosby's humerous show,  the other day I said the following to my daughter:

I'm sorry, donkeys don't stick to the refrigerator.

Taken out of context, Becky thought it was pretty funny. Here's the context: I was playing with Jenna in the kitchen the other day, and she was playing with 2 plastic donkeys as well as some refrigerator magnets.  After seeing how the magnets stuck to the refrigerator, she tried to do the same with the plastic donkeys, which didn't work of course.

posted on Wednesday, January 09, 2008 10:40:08 AM (Pacific Standard Time, UTC-08:00)  #    Comments [2]
 Thursday, January 03, 2008

The CTP for the MVC framework includes support for master-page, page, and user-control based views.  I thought it might be interesting to enable .ashx-based views for things like RSS generation via System.Xml.Linq, or other more "raw" view output.

As it turns out, this is fairly trivial.  The place we need to extend is the IViewFactory returned by Controller.ViewFactory.  This is the component that is responsible for creating the view when a call to RenderView is made.

The default view factory is the WebFormViewFactory, which knows how to generate views based on .master, .aspx, and .ascx views.  Since we want to add support for .ashx, we'll use WebFormViewFactory as a starting place.  We'll inherit from WebFormViewFactory and override CreateView to supply our extra .ashx lookup.

using System;
using System.Globalization;
using System.IO;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;

public class SpecialViewFactory : WebFormViewFactory {

    static readonly string[] ViewLocationFormats = new string[] { "~/Views/{1}/{0}.ashx", "~/Views/Shared/{0}.ashx" };

    ControllerContext _ControllerContext;

    #region IViewFactory Members

    protected override IView CreateView(ControllerContext controllerContext, string viewName, string masterName, object viewData) {
        _ControllerContext = controllerContext;
        //check to see if there is an ashx that matches here.
        object value = null;
        controllerContext.RouteData.Values.TryGetValue("controller", out value);
        string controllerName = value as string;
        if (controllerName == null) {
            throw new InvalidOperationException("No route data value available for controller.");
        }

        Type viewType = null;
        foreach (var loc in ViewLocationFormats) {
            var path = string.Format(CultureInfo.InvariantCulture, loc, viewName, controllerName);
            viewType = GetCompiledType(path);
            if (viewType != null) break;
        }
        if (viewType == null) {
            return base.CreateView(controllerContext, viewName, masterName, viewData);
        }

        if (!typeof(IView).IsAssignableFrom(viewType)) {
            //TODO: better exception
            throw new InvalidOperationException("Type not a view");
        }
        var view = (IView)Activator.CreateInstance(viewType);
        var viewHandler = view as ViewHandler;
        if (viewHandler != null) viewHandler.ViewData = viewData;

        _ControllerContext = null;
        return view;
    }

    private Type GetCompiledType(string path) {
        Type compiledType = null;
        try {
            if (File.Exists(_ControllerContext.HttpContext.Request.MapPath(path))) {
                compiledType = BuildManager.GetCompiledType(path);
            }
        }
        catch (HttpCompileException) {
            throw;
        }
        catch (HttpParseException) {
            throw;
        }
        catch (HttpException) {
        }
        return compiledType;
    }

    #endregion
}

GetCompiledType had to be replicated as it isn't exposed in the base class.  Note, I added a File.Exists check before I attempt to get the compiled type from the BuildManager. This was really to avoid having to deal with a bunch of first chance exceptions in the debug, although it seems likely that avoiding the exception is a good thing.  It wouldn't catch handlers that are mapped in the app dynamically or via web.config.

As you can see, I also added a ViewHandler class that my handlers can inherit from that gives them the same goodies that the other views get, but I'll leave that as an exercise for the reader to implement.

So, now the only thing remaining is to inject our special view factory into the pipeline instead of the default.  A simple way to do this is to set the ViewFactory property in the constructor of any controller that needs .ashx support. Now, you can create .ashx files and use them as views!

Next time, I'll show you how to add support for routing controller actions based on data not in the URL.

posted on Thursday, January 03, 2008 3:46:58 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Tuesday, December 11, 2007

markandjasonWell, my old-school buddy Jason turns 32 today.  Jason and I have been friends since pre-school.  We pretty much attended every year of school together, all the way through our undergraduate years.  Here's a picture of me and him (Jason on the left... I don't know what the heck I'm doing) at one of my brother's birthday parties.  He was the friend I got to invite.

Happy birthday, man.  Go play some slaughterball today for me.

posted on Tuesday, December 11, 2007 4:22:30 PM (Pacific Standard Time, UTC-08:00)  #    Comments [4]
 Monday, December 10, 2007

I've been loving playing around with the CTP of the MVC framework for ASP.net.  On several occasions in the past, I've rolled my own hybrid MVC implementations on top of ASP.net, so the prospect of a high-quality, "official" implementation is pretty exciting.

Note that I'm NOT on the ASP.net team.  I don't have any "inside" information about the framework, other than the privilege of getting to play with early bits.  But, since my previous jobs have involved alot of web development, I feel like I have some interesting insights.  I've been doing alot of app-building with the framework, and I found myself missing a few things that I'm sure are in the planning phases, but they are missing nonetheless.  Nikhil has some great AJAX support prototyped, so I was able to build on that, but I have some other samples I'll share that may be of some use.

First, users will no doubt be frustrated by trying to implement something simple like shared components.  Think of a blogroll, or list of categories on a blog.  These items don't really depend on input from the route (URL), they show the same data for every page.  Currently, each controller action has to put this data into the ViewData (which makes it more difficult to dynamically add shared components, especially if you're using strongly-typed ViewData).

You could solve this in your controllers by inheriting from a common controller type that prepared that data for you, but then you likely have a controller pulling data that is totally unrelated to it.

I think a better approach is to allow Views to render actions directly.  At first, this may seem like a violation of general MVC practices, but think of it this way...

  • A view can already generate a url to an action that would be rendered if the user clicked on it.
  • With proper AJAX support, you could make a callback and have the results of that action render into part of the page, either onclick or onload.
  • Allowing views to render actions using the same input that they would use to create a URL to that action simply eliminates the round-trip.

So, as Nikhil adds "RenderPartial" support to views, here's my RenderAction implementation:

First are some extension methods for the different view types.  These would go in an appropriate static class.

        /// <summary>

        /// Common logic for rendering actions as part of a view.

        /// </summary>

        static void RenderAction(string actionUrl, IHttpContext currentContext) {

            //create a fake IHttpContext that will fool the route collection

            //into creating RouteData for our action

            var fakeContext = new FakeContext(actionUrl, currentContext);

            var routeData = RouteTable.Routes.GetRouteData(fakeContext);

            //Make a new MvcHandler that will process our fake request

            var handler = new MvcHandler() {

                RequestContext = new RequestContext(fakeContext, routeData)

            };

            ((IHttpHandler)handler).ProcessRequest(HttpContext.Current);

        }

 

        /// <summary>

        /// Allows a page-based view to render actions

        /// </summary>

        public static void RenderAction(this ViewPage page, object values) {

            RenderAction(page.Url.Action(values), page.ViewContext.HttpContext);

        }

 

        /// <summary>

        /// Allows a master-based view to render actions

        /// </summary>

        public static void RenderAction(this ViewMasterPage page, object values) {

            RenderAction(page.Url.Action(values), page.ViewContext.HttpContext);

        }

 

        /// <summary>

        /// Allows a usercontrol-based view to render actions

        /// </summary>

        public static void RenderAction(this ViewUserControl control, object values) {

            RenderAction(control.Url.Action(values), control.ViewContext.HttpContext);

        }

You could imagine adding a similar extension to enable this for any kind of view.

Now, the "FakeContext" type is an implementation of IHttpContext that does the trickiness we need to get the routes to work, and the MvcHandler to execute the request.  Here's the relevant parts, the rest of the implementation just throws so that it is clear when something breaks that we haven't really thought through.

        class FakeContext : IHttpContext {

 

            //This is a similar fake object to help

            class FakeRequest : IHttpRequest {

                FakeContext _FakeContext;

 

                public FakeRequest(FakeContext context) {

                    _FakeContext = context;

                }

 

                public string AppRelativeCurrentExecutionFilePath {

                    get { return VirtualPathUtility.ToAppRelative(_FakeContext._ActionUrl); }

                }

 

                public string PathInfo {

                    get { return string.Empty; }

                }

 

                /// <summary>

                /// This is here to allow Nikhil's IsAjaxRequest method to continue to work

                /// </summary>

                public System.Collections.Specialized.NameValueCollection Headers {

                    get { return new System.Collections.Specialized.NameValueCollection(); }

                }

 

                //the rest of the implementation just throws.

                //that way, we know if something needs to be implemented later.

            }

 

            string _ActionUrl;

            IHttpContext _Context;

 

            public FakeContext(string actionUrl, IHttpContext context) {

                _ActionUrl = actionUrl;

                _Context = context;

            }

 

            public IHttpRequest Request {

                get { return new FakeRequest(this); }

            }

 

            public IHttpSessionState Session {

                get { return _Context.Session; }

            }

 

            //the rest of the implementation just throws.

            //that way, we know if something needs to be implemented later.

        }

This is a testament to the flexibility of the interface-based approach.  This would be MUCH more difficult if we had to deal with the concrete HttpContext type.

So, if we want to inject the results of an action in part of a view, you would just do something similar to the following in your view, and you can pass other data, just as if you were creating an Action.Link:

<% this.RenderAction(new { Controller = "Tags", Action = "Cloud" }); %>

Bingo, the system processes the request (almost) as if it were an isolated request.  There are obviously some limitations here, as well as some dependency on using Controller-based controllers and the MvcHandler, but that's certainly the common scenario.

Next time, I'll show you how to add support for ".ashx"-based views.

posted on Monday, December 10, 2007 12:44:29 PM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
 Sunday, December 02, 2007

CRW_6366It's Jenna's 2nd birthday!  Wow, I can't believe it's already been 2 years.  Here's one of the pictures we took yesterday for our Christmas cards.

That was really fun.  I don't have a nice flash, so I had to resort to auxiliary lighting.  All things considered, I think they turned out pretty well.

Jenna constantly amazes me with what she knows.  She was playing with some blocks this morning and I asked her how many blocks she had.  She said, "Let's count.  One... Two... Three... Four."  Wow.

She's counted as high as 20, and can say the entire alphabet.  She loves to play with toy animals and make them talk with each other.  She loves to play "knock knock", where she'll close a door, we'll knock and she'll let us in, grab our hand and pull us in for a "visit".  Fun times.

CRW_3639

posted on Sunday, December 02, 2007 3:47:37 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Monday, November 19, 2007

This release was exciting me before I made the move to Microsoft, and getting to be a part of it has made me even more excited about what we're giving our customers in this release.  If you're not familiar with Visual Studio, it is a suite of software development tools for creating just about any application or library you can think of.  I'll be writing more about this release in the coming weeks, but for now, go to www.microsoft.com/vstudio and check it out!

posted on Monday, November 19, 2007 9:29:03 AM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Wednesday, October 31, 2007

Vista's search rocks, but sometimes you're not interested in searching through the gigabytes of file contents, you just want to search on filename.  Good ol "dir /s".  But, you don't need to give up the indexed goodness for this.  Just use some of the special search syntax: "name:[string you're searching for]" in the search box.

posted on Wednesday, October 31, 2007 3:03:16 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]