Question

I have a workflow that uses signalR to update a page as data comes in from the service. Currently the flow looks like:

Service -> SignalR Hub           Controller
                       v      ^              v
                         View                   Partial

The top line is server side, bottom is client. I use signalR to reach my jquery function, that uses AJAX to get the partial view from the controller and update the page.

It seems like I could shorten this process to :

Service -> Controller -> SignalR Hub
                                     v
                                        View

Where the controller would send the string representation of the partial view to the SignalR Hub.

My problem is trying to get the string representation of the partial view. I've looked around and found this code:

protected string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = ControllerContext.RouteData.GetRequiredString("action");

        ViewData.Model = model;

        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }

But this requires the ControllerContext, which for me is always null as I am not calling this method from the client. I have not found a way to render the partial view without the ControllerContext. Is it possible to do this?

Was it helpful?

Solution

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);

OTHER TIPS

I found this one line code to work perfectly. orderModel being my model object. This one does not use ControllerContext but rather html context of the helper method.

System.Web.Mvc.Html.PartialExtensions.Partial(html, "~/Views/Orders/OrdersPartialView.cshtml", orderModel).ToString();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top