13

I am having trouble using "RenderPartialViewToString" without a controller class.

I am currently having to create HTML within application start up which requires making a model, making a view and turning the view in to a HTML string.

Within my view it uses HTML Helper function/extension which seems to only be there if a controller is there.

Can anyone shed some light on how I can do this?

tereško
  • 58,060
  • 25
  • 98
  • 150
Lemex
  • 3,772
  • 14
  • 53
  • 87

3 Answers3

7

You couldn't use html helper without current controller context.Try this extensions for render view into html

public static class RenderViewHelper
{
    public static string RenderPartialToString(string viewPath, object model)
    {
        string viewAbsolutePath = MapPath(viewPath);

        var viewSource = File.ReadAllText(viewAbsolutePath);

        string renderedText = Razor.Parse(viewSource, model);
        return renderedText;
    }

    public static string RenderPartialToString(ControllerContext context, string partialViewName, object model)
    {
        ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);

        var viewData = new ViewDataDictionary() { Model = model };

        if (result.View != null)
        {
            var sb = new StringBuilder();

            using (var sw = new StringWriter(sb))
            {
                using (var output = new HtmlTextWriter(sw))
                {
                    var viewContext = new ViewContext(context, result.View, viewData, new TempDataDictionary(), output);
                    result.View.Render(viewContext, output);
                }
            }

            return sb.ToString();
        }

        return string.Empty;
    }

    public static string MapPath(string filePath)
    {
        return HttpContext.Current != null ? HttpContext.Current.Server.MapPath(filePath) : string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, filePath.Replace("~", string.Empty).TrimStart('/'));
    }
}

First method used razor engine library. Second work with controller context.

7

Razor.Parse is deprecated now. With version 3.5 of the Razor engine you would follow the steps outlined here: https://antaris.github.io/RazorEngine/Upgrading.html

The text below is copied verbatim from the above link:

var result = Razor.Parse(razorTemplate, model, cache_name)

is now either (when the modeltype is known or you want to precompile on startup)

// Once at startup (not required when using an ITemplateManager which knows how to resolve cache_name)
Engine.Razor.AddTemplate(cache_name, razorTemplate)
// On startup
Engine.Razor.Compile(cache_name, typeof(MyModel) /* or model.GetType() or null for 'dynamic'*/)

// instead of the Razor.Parse call
var result = Engine.Razor.Run(cache_name, typeof(MyModel) /* or model.GetType() or null for 'dynamic'*/, model)

or (when you want lazy compilation, like Parse)

// Once at startup (not required when using an ITemplateManager which knows how to resolve cache_name)
Engine.Razor.AddTemplate(cache_name, razorTemplate)

// instead of the Razor.Parse call
var result = Engine.Razor.RunCompile(cache_name, typeof(MyModel) /* or model.GetType() or null for 'dynamic'*/, model)

The semantic equivalent one-liner would be (only to be used to get started with RazorEngine quickly):

// This will just call AddTemplate for you (every time), note that the ITemplateManager has to support AddTemplate
// and it has to handle multiple calls to AddTemplate gracefully to make this work.
// The default implementation will throw an exception when you use the same cache_name for different templates.
var result = Engine.Razor.RunCompile(razorTemplate, cache_name, model.GetType() /* typeof(MyModel) or or null for 'dynamic'*/, model
Tom Regan
  • 3,580
  • 4
  • 42
  • 71
5

A very nice answer to this question is Westwind.Web.Mvc.ViewRenderer.

Explain: If you want to render views outside MVC you need ControllerContext that is fully functional and Razor can get all information from it.

By using the ViewRenderer class you just can call this method to render view by passing model and view data dictionary:

    public class EmptyController : Controller { }

    public static string RenderRazorViewToString(string viewName, [Optional] object model,[Optional] ViewDataDictionary viewData)
    {
        var controller = ViewRenderer.CreateController<EmptyController>();
        controller.ViewData =viewData??new ViewDataDictionary();
        controller.ViewData.Model = model;
        var context = controller.ControllerContext;
        var html = ViewRenderer.RenderView(viewName, model, context);
        return html;
    }

I hope this would helpful.

Enjoy :)

pixparker
  • 2,903
  • 26
  • 23
  • EmptyController is included in ViewRenderer.cs source file. that can be removed. – pixparker Nov 27 '17 at 01:05
  • You'll need `HttpContext.Current` to have a fully functional value for ViewRenderer. That's fine so long you use it when the controller is running within a web server. For unit testing (no web server and HttpContext.Current == null) I tried to work around it but always ended up with the same NullReferenceException deep in the MVC code without further information. Instead I ended up using RazorEngine as it worked in both scenarios with and without HttpContext. – Manfred Dec 14 '17 at 16:30
  • @Manfred thanks. I had same problem with null HttpContext. do you have a working sample link for RazorEngine? – pixparker Dec 14 '17 at 20:59
  • Unfortunately, I don't have a working sample that I can share. In general RazorEngine is fairly straight forward and easy to use, though. – Manfred Jan 16 '18 at 05:27
  • 2
    RazorEngine is currently not working on .NET Core 2.0; RazorLight does not work with HTML Helpers or Tag Helpers, and the ControllerContext is not realizable, so it does not work if I need to schedule the task. So there does not seem to be a really good solution so far. – MovGP0 Jan 31 '18 at 16:15