As an alternative to using RenderAction() (which has some drawbacks because it must run the entire ASP.NET request pipeline to get its output), you can use a BaseController type that all of your controllers inherit which overrides OnActionExecuted() to insert values into the ViewData collection. With this approach you will then have the benefit of using a single request, and still not having to worry about manually adding cross cutting data to your model every time you handle a request.
To keep things simple I like to use a public const string SomeDataItemViewDataKey = "Controller.DataName";
in the controller class definition to key the ViewData entry added by the controller and then in the view when I need to render that output I can use templated helpers to pull the value from the ViewData: <%=Html.DisplayFor(ControllerType.SomeDataItemViewDataKey, "PartialViewUsedToRenderTheData") %>
.
Update
Because there's been some confusion about the validity of my statement, here is the original source of the performance claims against RenderAction():
Yes, there is a signficant difference
in performance of RenderAction
(slower) vs. RenderPartial (faster).
RenderAction, by definition, has to
run the whole ASP.NET pipeline to
handle what appears to the system to
be a new HTTP request, whereas
RenderPartial is just adding extra
content to an existing view.
-Brad Wilson, Senior developer on the ASP.NET MVC team
Quote source: http://forums.asp.net/p/1502235/3556774.aspx#3556590
Brad Wilson's blog: http://bradwilson.typepad.com/
Update 2
Here's the code from RenderAction() in the MVC2 RTM sources where we can see that although there is a new request being fired, it actually isn't going through the whole ASP.NET pipeline anymore. That being said, there are still some minor although mostly negligible drawbacks to using it over the alternative of PartialViews and ViewModel/ViewData. From what I understand (now) there was an overhaul on the implementation of RenderAction() prior to being moved from the MvcFutures assembly into the core framework; so it may hold that the statement above from Brad Wilson was more valid when he made it 6 months ago than it is now.
internal static void ActionHelper(HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, TextWriter textWriter) {
if (htmlHelper == null) {
throw new ArgumentNullException("htmlHelper");
}
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
routeValues = MergeDictionaries(routeValues, htmlHelper.ViewContext.RouteData.Values);
routeValues["action"] = actionName;
if (!String.IsNullOrEmpty(controllerName)) {
routeValues["controller"] = controllerName;
}
bool usingAreas;
VirtualPathData vpd = htmlHelper.RouteCollection.GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, null /* name */, routeValues, out usingAreas);
if (vpd == null) {
throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
}
if (usingAreas) {
routeValues.Remove("area");
}
RouteData routeData = CreateRouteData(vpd.Route, routeValues, vpd.DataTokens, htmlHelper.ViewContext);
HttpContextBase httpContext = htmlHelper.ViewContext.HttpContext;
RequestContext requestContext = new RequestContext(httpContext, routeData);
ChildActionMvcHandler handler = new ChildActionMvcHandler(requestContext);
httpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(handler), textWriter, true /* preserveForm */);
}