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]
 Monday, October 15, 2007

I certainly don't want this to turn into a video blog of fish, but I got some more video of the fish in our stream, and it's so unbelievable to me as someone new to this area that I simply must post it.  I've also done some tweaks to my custom Silverlight player as well as used some different encoding techniques (I haven't decided whether I like them yet).

Again, enjoy me sounding like an idiot.

[UPDATE:] The way I have embedded the player this time seems to prevent it from showing in most RSS aggregators. click through to my blog to see the video.

posted on Monday, October 15, 2007 2:02:31 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Wednesday, September 26, 2007

Here I am sticking Dan while he tries to score a flag capture.  Halo 3 rocks.  This is a screenshot of real gameplay.

[I ended up uploading to Flickr while I figure out how to link to the screenshot on bungie's servers]

[UPDATE] Some people have complained about my "real gameplay" statement.  Let me clarify.  After the game, I replayed the film of the game, during which I paused at this frame, detached the camera, took this picture (which automatically makes it available from bungie.net).  I am the guy at the top of the ramp.  I've just thrown a grenade toward Dan, which subsequently stuck to him and killed him.  So, no this is not what I saw during the game, but if someone else was in the game with there head where my camera was, you would have seen this.  My point was that it is not a "cutscene", manually posed render or other pre-rendered sequence (although you could argue the pre-rendered point since screenshots are slightly higher resolution than is being processed in-game).

posted on Wednesday, September 26, 2007 8:18:35 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Saturday, September 22, 2007

So, the salmon are running in our stream.  They're really big.  I caught this video today of a smaller female (I think).  Hopefully I'll get some bigger ones on video soon.  This is also an experiment with Silverlight.

Oh, by the way.  I'm like giddy with the thought of salmon in my stream, so I sound like an idiot.

posted on Saturday, September 22, 2007 6:57:50 PM (Pacific Standard Time, UTC-08:00)  #    Comments [2]
 Wednesday, August 29, 2007

I wish I had time to come up with more concrete information (examples/code) in this post, but I don't have the time to work that stuff up.  I did think it would be useful for people searching for solutions to this problem, so here it is in all its ambiguity.

I was playing with expression tree inspection and dynamic interpretation the other day, when I hit something that I was sure was a bug.  I was inspecting an expression tree, identifying "branches" of interest, and generating lamdba expressions from them on the fly.

You might do this to break up an expression into separate units of execution to spread across multiple processors (a la PLINQ), or to replace parts of a tree requiring local execution before passing off to another layer to be transformed into another domain like SQL, or whatever.  In any case, I was doing it.

I found that if the type of the expression was a value type, I could not create a lambda expression returning object from it, even though there is an inheritance relationship.  You get a fairly straightforward, but perhaps surprising exception.

After some back and forth with the Linq team, I discovered that this was by design.  In the case of value types, the boxing operation required to make an object must be represented by a unary convert expression. The solution is to wrap such expression trees with a call to Expression.Convert(expression, typeof(object)).

posted on Wednesday, August 29, 2007 4:17:11 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]