Home MVC Storefront

ASP.NET MVC Preview 4: ComponentController Is Now RenderAction

I found out the other day (the hard way) that ComponentController - my beloved little prototype - was ripped mercilessly from the MVC framework in Preview 4 and cast into the Pit Of Carkoon to "be slowly digested over thousands of years". I was mortified, vaklempt, the refactor dingo ate my baby! Incensed, I emailed that Eilon guy and said "you better run before I WTFPWN!" and he casually said "relax you little drama queen. I do what I always do... made your cruft shine". And so he did...

The Debate
We had a good thread going (among others) on the forums for a while about reusable controls and one of the ideas brought up by tgmbdm was to use something like "RenderAction" - wherein you render an Action to a page in a specific location. It's something that the Phil's team was working on for a while - and I brought this idea up to them and they said "yah - definitely want to do something like that. You do it".

So I prototyped up the ComponentController and it did what it was supposed to do - but kind of languished for a bit (for so long as a matter of fact that Phil forgot it was in there).

The issue that inevitably came up was "is this MVC?". Meaning - should a View be able to call a Controller's action? I'm going to sidestep that question because, well, encapsulating logic is needed.

The Code
In the MVC Storefront I had a nice ComponentController which output the category nagivation list on the left side of the screen. It worked nicely, and I could inject it with the right dependencies using StructureMap. The thing I couldn't do, however, was test it since ComponentController didn't implement an interface or base class. This was by design - there were some gymnastics I had to do to get the thing to render properly and... well I'll just leave it at that.

With the recent changes to Preview 4, I was able to completely remove the whole mess from the application and simply add an Action to the CategoryController:

 

        public ActionResult CategoryList()
        {
            return View("_CategoryList", _catalogService.GetCategories());
        }

Notice I use a "_" to denote a partial view - this is a convention used by a lot of view engines and so I'm keeping with it. To use it in a page, I can now simply:

        <%Html.RenderAction<CatalogController>(x=>x.CategoryList()) %>

Radical. This has so many nice implications to it and I think it's one of the coolest new features of Preview 4. Aside from all that Ajax grooviness!

Chris avatar
Chris says:
Wednesday, July 16, 2008

This is kinda cool, well until you consider that someone can navigate to the partial via URL.

So now instead of getting a 404 or a pretty page with a nice nav, they get marooned on a table of data with no real document wrapping it.

Is there protection for this?

What user input is RenderAction responding to, since thats the purpose of an action, therefore actions calling actions for render encapsulation, is called web user controls, remember those they're nasty.


Rob Conery avatar
Rob Conery says:
Wednesday, July 16, 2008

Chris there are plenty of ways to route your app so this doesn't happen. In terms of "what user input" - RenderAction() is for components - things that need encapsulated logic.

>>>actions calling actions for render encapsulation, is called web user controls, remember those they're nasty.<<<

You don't need to use them...


Haacked avatar
Haacked says:
Thursday, July 17, 2008

Actually, *I* was the one who "made your cruft shine". Now RenderAction is not theoretically tied to any specific view engine, though NHaml (which I tried it on) will need to make some small implementation tweaks to use it. ;)


Shannon avatar
Shannon says:
Thursday, July 17, 2008

I really like this and I hope it migrates into the core MVC. Is it strict MVC? Maybe not, though I think it could be argued either way, but it's one of those cases where if you don't have an option to do this, you are really restricted. Just a note though in your example above, RenderAction uses the internal MvcHandler and returns void. So, %=Html... doesn't work. You need to use %Html.RenderAction<>();% to call the method.


Jamie avatar
Jamie says:
Thursday, July 17, 2008

I've been whining on all of the other blogs about this, so might as well here too... Seems to me that subcontrollers, or whatever you want to call this, is something that many, many people are wondering about, but it's all being pushed aside for more "flashy" stuff like a bunch of AJAX helpers. Does anyone working with a good AJAX library really need helpers or can't write their own pretty easily? THIS stuff, the stuff on this page, the componentization stuff, is the stuff that really requires some serious architecting and is something alot of us would appreciate some guidance for.

Hopefully we can at least get a blog post from someone giving us an idea where they're heading so we can roll our own that at least gets close to what the final might be.


Tim Barcz avatar
Tim Barcz says:
Thursday, July 17, 2008

@Shannon ... you're a savior (I was getting errors because I had <%=.

@Rob, you had an extension method that allowed you to pull an instance from your IoC container. Can you do that anymore or no?

Tim


Rob Conery avatar
Rob Conery says:
Thursday, July 17, 2008

@Jamie great feedback - I'll forward to the team. It's not being pushed aside per se; it's just not MVC unfortunately (from the view on the mountain top) and so the team is trying to figure out how to make it so.

@Tim - since these are now just regular controllers (spun up by the controller factory which StructureMap controls) I get IoC for free :).


tgmdbm avatar
tgmdbm says:
Thursday, July 17, 2008

Hey there Rob, could you correct your smelling pistake please?

Yeah, I've actually gone off this approach. Because you're trying to encapsulate the logic of your component, it made sense to put methods on the component controller which retrieved the data from its form fields (assuming your component has form elements). This created a dependency on the component controller from the main one which made it a nightmare to test.

Since this has gone to original way of working, I'll see if i can reimplement it to be more testable.

Looking forward to the next incarnation of this feature.


Dan avatar
Dan says:
Friday, July 18, 2008

Hey Rob,

I don't think there should be a "=" before Html.RenderAction:

<%Html.RenderAction<CatalogController>(x=>x.CategoryList()) %>


John Haigh avatar
John Haigh says:
Friday, July 18, 2008

I downloaded Preview 4 and upon digging into System.Web.MVC there is no Html.RenderAction as you have above but as I have read in the documentation RenderAction is apart of MVC Futures. So RenderAction in MVC Futures are rolled into Microsoft.Web.Mvc.ViewExtensions.RenderAction, so how are you calling Html.RenderAction? i.e. <% Html.RenderAction<BlogController>(x=>x.List()); %>

Wouldn't you need to call RenderAction like this:

<% Microsoft.Web.Mvc.ViewExtensions.RenderAction<BlogController>(x=>x.List()); %>

Just curious....is there is alias somewhere to Microsoft.Web... for Html somewhere?


Shannon avatar
Shannon says:
Friday, July 18, 2008

I pointed out the = problem above, not sure why Rob hasn't fixed it in the post yet. You'll get 'cannot convert void to object errors if you use the =.

@John: You need to add namespace="Microsoft.Web.Mvc" in your web.config file. Once you do that, you can reference the RenderAction method as an extension to Html.


paul avatar
paul says:
Saturday, August 02, 2008

So, checking out the RenderAction, I can see why the debate rages about the MVC-ishness of it (or lack thereof).

My concern is more of a practical one. I tried it out using the lambda-based overload first (Html.RenderAction<SomeController>{c=>c.Action());) and everything worked as expected.

When I tried it using the string-based overload, however, (Html.RenderAction("action","controller")), I got a StackOverflow exception, pointing to infinite recursion. Setting a breakpoint on the RenderAction line and stepping into it, I see that, sure enough, I'm recursing infinitely. What strikes me as odd is that it keeps rolling to my HomeController's Index action (the /Home/Index.aspx view is where RenderAction is being called). Anyone experienced this? Is it a bug, or something I'm not doing right?

Paul


Rob Conery avatar
Rob Conery says:
Saturday, August 02, 2008

It could be a routing thing - I would imagine the team would catch this. So to be clear - you're calling a method on the same controller, which is calling the page that RenderAction is on, which is calling the method on the controller, etc?

I'd check your routes and make sure the right method is called.


Chace avatar
Chace says:
Saturday, August 02, 2008

Eh.. I don't think this the right route to take for rendering components. ^ Paul (paul.vencill) came up with a better approach for mvcms in my opinion. Rather than giving the view access to the controller hes rendering them based on a URL. I personally think his approach adheres to the MVC framework and opens the door for a few other creative options.


paul avatar
paul says:
Sunday, August 03, 2008

thanks, Chance.

@Rob,

No, to be clear, I'm in the Index View of the HomeController, and in a test site, I'm trying to call the Details action of the EmployeeController. The syntaxes I've tried are

Html.RenderAction<EmployeeController>(c=>c.Details());

and

Html.RenderAction("Details","Employee");

The first works fine (which makes me think it's not a routing issue) where the second goes StackOverflow for infinite recursion. I didn't do anything funky with my routes, just leaving the defaults in place, as I built this project for no reason other than to play with the new RenderAction method to compare it to the one I ginned up in Preview 3 (that ^Chace mentioned).



Search Me
Subscribe

Index Of MVC Screencasts

You can watch all of the MVC Screencasts up at ASP.NET, and even leave comments if you like.

Popular Posts
 
My Tweets
  • @haacked must.... resist... assimilation...
  • Dinner at the Haacks. How did Phil get such a cute kid? Evidently Phil's in the doghouse though...
  • @shanselman dude turn off twitter and drive! that's gotta be illegal!
  • For D'Arcy and Justice... Scottgu goes Canuck! http://twitpic.com/mfz1
  • Working in ScottGu's office with @shanselman. Wearing an Orange Polo and saying "go ahead" a lot for some reason.
  About Me



Hi! My name is Rob Conery and I work at Microsoft. I am the Creator of SubSonic and was the Chief Architect of the Commerce Starter Kit (a free, Open Source eCommerce platform for .NET)

I live in Kauai, HI with my family, and when my clients aren't looking, I sometimes write things on my blog (giving away secrets of incalculable value).