Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor engine can't find my view in .Net Core class library

I have my class library, which consists of ViewRenderService class:

public interface IViewRenderService
    {
        Task<string> RenderToStringAsync(string viewName, object model);
    }

    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;

        public ViewRenderService(IRazorViewEngine razorViewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _razorViewEngine = razorViewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

            using (var sw = new StringWriter())
            {
                var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);

                if (viewResult.View == null)
                {
                    throw new ArgumentNullException($"{viewName} does not match any available view");
                }

                var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                {
                    Model = model
                };

                var viewContext = new ViewContext(
                    actionContext,
                    viewResult.View,
                    viewDictionary,
                    new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                    sw,
                    new HtmlHelperOptions()
                );

                await viewResult.View.RenderAsync(viewContext);
                return sw.ToString();
            }
        }

and a lot of views, which start from the path: my class library root/Views//Shared/many views.

The problem is, that IRazorViewEngine can't find my views, how should I call viewRenderService.RenderToStringAsync(?) to render ~/Views/Shared/Myview.cshtml, for example?

like image 965
Yurii N. Avatar asked Aug 31 '25 00:08

Yurii N.


2 Answers

The way I handle views in class libraries is to make the views embedded resources, ie in the .csproj file I have

<ItemGroup>
  <EmbeddedResource Include="Views\**" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
</ItemGroup>

and you need this package:

<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="1.1.*" />

and then I have an extension method in my class library like this:

public static RazorViewEngineOptions AddCloudscribeSimpleContentBootstrap3Views(this RazorViewEngineOptions options)
{
    options.FileProviders.Add(new EmbeddedFileProvider(
        typeof(Bootstrap3).GetTypeInfo().Assembly,
        "cloudscribe.SimpleContent.Web.Views.Bootstrap3"
    ));

    return options;
}

and then in Startup.cs of the main app you have to opt in to including those views using the extension method like this:

services.AddMvc()
.AddRazorOptions(options =>
{
    options.AddCloudscribeSimpleContentBootstrap3Views();
});
like image 133
Joe Audette Avatar answered Sep 03 '25 00:09

Joe Audette


Update for .Net Core 3.1

After struggling for a while myself with the same problem @Jean's answer was of great help. Here's my .Net Core 3.1 update of that answer. Hope it will help other people.

This is in the .csproj file of the .Net Core 3.1 class library:

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>  
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>

  <ItemGroup>
    <Content Update="Views\**\*.cshtml" />
  </ItemGroup>

</Project>

This is the file structure in the library.

enter image description here

To invoke the View Component in the library from the web app use this:

 @await Component.InvokeAsync("GoogleAnalytics")
like image 22
Roger Avatar answered Sep 02 '25 23:09

Roger