Search code examples
c#asp.netasp.net-mvcviewbag

Transform partial view into string with calling controller's action


I have MVC controller with action that returns partial view. Action contains a lot of logic and sends data in view through ViewBag object.

I need to render result of this action into string and then send as email. All solutions that I found render view without action calling.

Is it possible to init controller somewhere in my code, call action method and transform ActionResult (that is partial view) into string?

Thx


Solution

  • I did it. With a lot of pain.

    At first we need to init controller without context. For this I use Rick Strahl's method called CreateController

     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 = null;
            if (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;
        }
    

    Source: http://weblog.west-wind.com/posts/2013/Jul/15/Rendering-ASPNET-MVC-Razor-Views-outside-of-MVC-revisited

    But if you call this method when HttpContext it will throw exception. To prevent I init fake context using this code: https://stackoverflow.com/a/19709225/1850880.

    Then I init controller and call action. Result of this action contain viewData with all data passed from action. We can get it with reflection (like a ninja). And then we can render view to string with passed model.

            var controller = CreateController<ControllerName>();
            var result = controller.ActionName(param1, param2);
            object viewData = result.GetType().GetProperty("ViewData").GetValue(result);
            object values = viewData.GetType().GetProperty("Values").GetValue(viewData);
            var str = ec.RenderRazorViewToString("ViewName", values);
            return str;
    

    This method render view to string:

     public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
        {
            controller.ViewData.Model = model;
            controller.ViewBag.Model = model;
            using (var sw = new StringWriter())
            {
                var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
                var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                viewResult.View.Render(viewContext, sw);
                viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
                return sw.GetStringBuilder().ToString();
            }
        }
    

    Looks like it's not the best way but it works. So if you have any ideas be sure to write.