0

I need to render a specific MVC view in my app (for a couple hundred database records - one view rendered per record) each as a string so that I can save them out to *.html files for offline viewing.

I have found several sources on how to do this:

How to render an ASP.NET MVC ViewResult to HTML?

and

http://www.west-wind.com/weblog/posts/2012/May/30/Rendering-ASPNET-MVC-Views-to-String

which seem to work fine with just basic views. My problem is that my view makes calls to Html.DisplayFor (which renders my custom display templates), and those templates also make calls to Html.DisplayFor. When I am using the methods in the links above, it seems as a default display template is being used and none of MY HTML is being rendered for those areas of my views.

Note that the views that I am attempting to render as string render just fine with the custom display templates when letting MVC render them out to a ViewResult and displaying them on screen.

Is there any way to render an MVC view as a string so that it can be saved out to file where the custom display templates are used instead of the default? I would prefer not to have to create a new view specifically for this purpose that contains no custom display templates. Any help is greatly appreciated.

Here is an example of my code stripped to just show the most basic functionality. Again, this works fine for basic views, just not with my custom display templates.

public void GenerateOfflinePackage(IndexViewModel model, ControllerContext controllerContext)
{
    var folderName = Guid.NewGuid().ToString();
    var tempPackagePath = string.Format("{0}\\{1}", this.hostingEnvironmentWrapper.MapPath("~/App_Data"), folderName);

    Directory.CreateDirectory(tempPackagePath);

    var cpfModel = this.careerPlanningFormService.BuildSummaryViewModel(59);
    var viewString = this.RenderRazorViewToString("~/Views/CPF/Summary.cshtml", cpfModel, controllerContext);

    using (StreamWriter outfile = new StreamWriter(tempPackagePath + @"\59.html", true))
    {
        outfile.Write(viewString);
    }
}

public string RenderRazorViewToString(string viewName, object model, ControllerContext ctxt)
{
    var s = string.Empty;
    var viewData = ctxt.Controller.ViewData;
    var tempData = ctxt.Controller.TempData;

    viewData.Model = model;

    using (var sw = new StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(ctxt, viewName);
        var viewContext = new ViewContext(ctxt, viewResult.View, viewData, tempData, sw);
        viewResult.View.Render(viewContext, sw);
        viewResult.ViewEngine.ReleaseView(ctxt, viewResult.View);
        s = sw.GetStringBuilder().ToString();
    }

    return s;
}

Example ViewModel and some included classes:

    public class SummaryViewModel : CareerPlanningFormBase
{
    public string StaffName { get; set; }

    public string StaffImageUrl { get; set; }

    public string PredominantRole { get; set; }

    public IList<SummaryResultsMeasuresSectionModel> ResultsMeasuresSections { get; set; }
}

public class SummaryResultsMeasuresSectionModel
{
    public string SectionName { get; set; }

    public string SectionLabelTitle { get; set; }

    public string SectionLabelText { get; set; }

    public IList<SummaryResultsMeasureModel> ResultsMeasures { get; set; }
}

public class SummaryResultsMeasureModel
{
    public string ResultsMeasureName { get; set; }

    public IEnumerable<string> MeasureTypes { get; set; }

    public IList<SummaryResultsMeasureCriterionModel> ResultsMeasureCriteria { get; set; }

    public IEnumerable<SummaryActionPlanModel> ActionPlans { get; set; }
}

1 Answer 1

1

Were I you, I'd take this from a different approach. Do Child Action Method Output Caching and then create a custom cache provider that saves to and reads from disk. This way you'd take the rendered output and store it to disk. I have a blog entry that walks you through it.

http://www.haneycodes.net/custom-output-caching-with-mvc3-and-net-4-0-done-right

Sign up to request clarification or add additional context in comments.

6 Comments

I may be missing something, but I don't see how this will help. I am not doing this on a per page/per request basis. I am clicking a button and calling a method that will iterate through hundreds of records and for each record, render out a view and save the resulting HTML as a file. Ultimately resulting in a couple hundred HTML files - all in a single request. The end game is that the app will then generate an index file linking to all of these files and it will all be zipped up and provided to users to review the data while traveling and offline (i.e. in-flight). That is the easy part.
The view that is generated - does it come from an ActionResult / ActionMethod on a controller?
The user who is generating the offline package will click a button on an admin screen. The controller action method handling the POST will make a service method call to GenerateOfflinePackage. That method will do all of the work, generate the views, save them, etc. (see my original post for an example doing just one record). The controller handling the POST, after the service method completes, simply does a redirect for the user.
If you're saying your Action Method spawns MANY outputs/saves, then the output caching probably won't help you much.
Yes, that is correct, it does MANY. Thank you for your help, though.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.