The answer linked in the comments is one way to do it. I stumbled upon another way through this page: creating a bogus controller context. I just implemented this myself in the last few weeks and it is working well.
public static T CreateController<T>(RouteData routeData = null)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = new T();
// get context wrapper from HttpContext if available
HttpContextBase wrapper;
if (System.Web.HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no "+
"active HttpContext instance is available.");
if (routeData == null)
routeData = new RouteData();
// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") &&
!routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller",
controller.GetType()
.Name.ToLower() .Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
So, you'd update your code like so:
protected string RenderPartialViewToString(ControllerContext context, string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = context.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
ViewContext viewContext = new ViewContext(context, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
And call it like so:
//can include RouteData parameter here if needed...
var bogusController = Util.CreateController<YourControllerType>();
var partialViewGuts = RenderPartialViewToString(bogusController.Context, "view", model);