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?
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();
});
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.
To invoke the View Component in the library from the web app use this:
@await Component.InvokeAsync("GoogleAnalytics")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With