16

I'm trying to render a HTML from a view without using a web request. I need the HTML as a string, internally, I do not wish to serve it.

The viewEngine.FindView() returns a viewEnineResult that shows no view was found. It shows to search locations where it looked they look like this:

/Views//PDFOperationsReportView.cshtml

/Views/Shared/PDFOperationsReportView.cshtml

(Observe the double forward slash in the first line)

File structure (I placed it into a HTML snippet cause I couldn't manage to format the text properly in this editor)

Project 
      Folder 
        Subfolder
            CodeFile.cs
      Views
        PDFOperationsReportView.cshtml

The code:

var viewName = "PDFOperationsReportView";
var actionContext = GetActionContext();
var viewEngineResult = _viewEngine.FindView(actionContext, viewName, false);
if (!viewEngineResult.Success)
{
    throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));
}
            
var view = viewEngineResult.View;
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Daarwin
  • 2,896
  • 7
  • 39
  • 69

2 Answers2

16

I had the same issue. I found the answer here: GitHub aspnet/Mvc Issue #4936

Basically, use GetView instead of FindView, like this:

var viewResult = razorViewEngine.GetView(viewName, viewName, false);

Your viewName needs to be a full path for this to work. For example:

  1. /Views/Shared/PDFOperationsReportView.cshtml
  2. ~/Pages/Shared/_Article.cshtml
  3. ~/Areas/CM/Pages/_Article.cshtml
carlin.scott
  • 6,214
  • 3
  • 30
  • 35
Matt
  • 6,787
  • 11
  • 65
  • 112
  • Just something that you missed it to make the path relative like '~/bin/Debug/Views/MainView.cshtml'. – Dragos Durlut Nov 01 '19 at 10:04
  • 1
    In case your views are in a class library, you find problems with caching and the layout not being updated or found. Solution is to setup the engine inside the Startup folder. Exmaple: https://stackoverflow.com/a/58659691/249895 – Dragos Durlut Nov 01 '19 at 14:51
  • 1
    This solution is working in .Net Core... Thanks – Shyam Bhagat Mar 26 '22 at 14:40
  • You should set the last parameter to true. otherwise it will not include thee layout if the view has one – Squibly Mar 06 '23 at 04:35
2

We have a helper method defined to render optional views which may or may not exist:

public static Task RenderPartialAsyncIfExists(this IHtmlHelper htmlHelper, ICompositeViewEngine engine, string partialViewName, object model)
{
    if (engine.GetView(partialViewName, partialViewName, false).Success)
    {
        return htmlHelper.RenderPartialAsync(partialViewName, model);
    }

    return Task.CompletedTask;
}

It's used on view pages like:

@inject ICompositeViewEngine Engine
...
@{ await Html.RenderPartialAsyncIfExists(Engine, $"~/Views/Shared/_navigationAdmin.cshtml"); }

This works find locally (IIS Express) but for some reason was failing when deployed to IIS.

In my case, there was something wrong with the .csproj file, where the view in question was removed but then re-added as an embedded resource:

    <ItemGroup>
        <Content Remove="Views\Shared\_navigationAdmin.cshtml" />
    </ItemGroup>
    
    <ItemGroup>
        <EmbeddedResource Include="Views\Shared\_navigationAdmin.cshtml" />
    </ItemGroup>

Removing those two sections from the .csproj fixed the problem in IIS.

This is using (EOL) AspNet Core 2.2

BurnsBA
  • 4,347
  • 27
  • 39