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?
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With