0

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

  • If you are using this for emails you may want to look into something like [MvcMailer](https://www.nuget.org/packages/MvcMailer) which pretty much does what you are looking for. I have had success with it in the past and it is easy to implement. – zgood Mar 29 '16 at 14:52

1 Answers1

0

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.

Community
  • 1
  • 1