Search code examples
asp.net-mvcasp.net-mvc-4signalrsignalr-hub

signalR send partial view result string via hub


I need to be able to return partial view as string in my MVC app via signalR. I'm using hubs.

I'm using the following method to return a partial view string (from here):

    public static string RenderPartialView(string controllerName, string partialView, object model)
    {
        var context = httpContextBase as HttpContextBase;

        var routes = new RouteData();
        routes.Values.Add("controller", controllerName);

        var requestContext = new RequestContext(context, routes);

        string requiredString = requestContext.RouteData.GetRequiredString("controller");
        var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
        var controller = controllerFactory.CreateController(requestContext, requiredString) as ControllerBase;

        controller.ControllerContext = new ControllerContext(context, routes, controller);

        var ViewData = new ViewDataDictionary();

        var TempData = new TempDataDictionary();

        ViewData.Model = model;

        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialView);
            var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, ViewData, TempData, sw);

            viewResult.View.Render(viewContext, sw);
            return sw.GetStringBuilder().ToString();
        }
    }

In order for this method to work I need HttpContext.Current therefore in my OnConnected (I noticed that this always exists) I set it like so:

    public class TaskActionStatus : Hub
    {
        private static HttpContextBase httpContextBase;
...

        public override Task OnConnected()
        {
            httpContextBase = new HttpContextWrapper(HttpContext.Current) as HttpContextBase;
...

and then I use it in my RenderPartialView method:

var context = httpContextBase as HttpContextBase;

This way I always have access to current HttpContext. However, I noticed that sometimes my static copy of HttpContext is null. Why is that?.

  • What's the best approach here?
  • Is there a way to render partial view without HttpContext?

Solution

  • I used fake http context to generate view:

        public static string GetRazorViewAsString(object model, string filePath)
        {
            HttpContext httpContext = MockHelper.FakeHttpContext();
    
            var st = new StringWriter();
            var context = new HttpContextWrapper(httpContext);
    
            var routeData = new RouteData();
            var controllerContext = new ControllerContext(new RequestContext(context, routeData), new FakeController());
            var razor = new RazorView(controllerContext, filePath, null, false, null);
            razor.Render(
                new ViewContext(controllerContext, razor, new ViewDataDictionary(model), new TempDataDictionary(), st), 
                st);
            return st.ToString();
        }
    
        #endregion
    }
    
    public class FakeController : Controller
    {
    }
    
    public class MockHelper
    {
        #region Public Methods and Operators
    
        public static HttpContext FakeHttpContext()
        {
            var httpRequest = new HttpRequest(string.Empty, "http://novomatic/", string.Empty);
            var stringWriter = new StringWriter();
            var httpResponce = new HttpResponse(stringWriter);
            var httpContext = new HttpContext(httpRequest, httpResponce);
    
            var sessionContainer = new HttpSessionStateContainer(
                "id", 
                new SessionStateItemCollection(), 
                new HttpStaticObjectsCollection(), 
                10, 
                true, 
                HttpCookieMode.AutoDetect, 
                SessionStateMode.InProc, 
                false);
    
            httpContext.Items["AspSession"] =
                typeof(HttpSessionState).GetConstructor(
                    BindingFlags.NonPublic | BindingFlags.Instance, 
                    null, 
                    CallingConventions.Standard, 
                    new[] { typeof(HttpSessionStateContainer) }, 
                    null).Invoke(new object[] { sessionContainer });
    
            return httpContext;
        }
    
        #endregion
    }