Reputation: 2411
I am currently busy building an email engine that uses the Razor templating system to provide the email templates. I posted this question yesterday which I solved.
Now the problem seems to be that when I include a partial view inside of my view it cannot be found. I am trying to include the partial view inside of my view as follows:
@await Html.PartialAsync("~/Views/Shared/EmailButton.cshtml", new EmailButtonViewModel("Confirm Account", "https://google.com"))
I have tried removing the ~
with no effect, I have used reflection to get the entire path to the partial view and passed that onto the PartialAsync
and that didn't work either. I have tried adding the entire path to the shared views folder in the startup.cs
as follows:
services.Configure<RazorViewEngineOptions>(o =>
{
var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
o.ViewLocationFormats.Add("~/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
});
In the above code I have tried replacing ~
with dir
to specify the entire location to the folder which didn't work either. My email templates are inside of a .NET Core class library and they have all their Build Action
set to Content
and Copy to output directory
to Copy always
I am unsure of what still to try.
You can see the exact error below:
The folder structure of the class library is as follows:
My startup.cs
looks as follows (removed unnecessary parts for the sake of brevity):
services.AddScoped<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
services.AddScoped<Email>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.Configure<RazorViewEngineOptions>(o =>
{
var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
o.ViewLocationFormats.Add("~/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
});
The code to render the view and convert it to a string is called like this:
await _razorViewToStringRenderer.RenderViewToStringAsync("Views/Emails/NewOrder/NewOrder.cshtml", newOrderModel);
The RenderViewToStringAsync
is as follows:
public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
{
var actionContext = GetActionContext();
var view = FindView(actionContext, viewName);
using (var output = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return output.ToString();
}
}
My FindView
code is as
private IView FindView(ActionContext actionContext, string viewName)
{
var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var getViewResult = _viewEngine.GetView(executingFilePath: dir, viewPath: viewName, isMainPage: true);
if (getViewResult.Success)
{
return getViewResult.View;
}
var findViewResult = _viewEngine.FindView(actionContext, viewName, isMainPage: true);
if (findViewResult.Success)
{
return findViewResult.View;
}
var searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations);
var errorMessage = string.Join(
Environment.NewLine,
new[] {$"Unable to find view '{viewName}'. The following locations were searched:"}.Concat(
searchedLocations));
throw new InvalidOperationException(errorMessage);
}
Upvotes: 1
Views: 1803
Reputation: 1271
Have you tried:
@await Html.PartialAsync("EmailButton", new EmailButtonViewModel("Confirm Account", "https://google.com"))
Upvotes: 1