|
using System; |
|
using System.Collections.Concurrent; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Threading.Tasks; |
|
using RazorEngineCore; |
|
|
|
namespace MyCompany.Templating.RazorEngineCore |
|
{ |
|
public class RazorCoreTemplateEngine : ITemplateEngine |
|
{ |
|
private readonly IRazorEngine _razorEngine; |
|
private readonly ConcurrentDictionary<string, object> _htmlCache = new(); |
|
private readonly ConcurrentDictionary<string, object> _nonHtmlCache = new(); |
|
|
|
public RazorCoreTemplateEngine( |
|
IRazorEngine razorEngine) |
|
{ |
|
_razorEngine = razorEngine; |
|
} |
|
|
|
public ValueTask<string> RunForHtmlAsync<TViewModel>( |
|
string viewTemplateName, |
|
string viewTemplate, |
|
TViewModel viewModel, |
|
string? layoutTemplateName = null, |
|
string? layoutTemplate = null, |
|
IDictionary<string, string>? includes = null) |
|
{ |
|
var compiledTemplate = GetCompiledHtmlTemplate<TViewModel>( |
|
viewTemplateName, |
|
viewTemplate, |
|
layoutTemplateName, |
|
layoutTemplate, |
|
includes); |
|
|
|
var result = compiledTemplate.Run(viewModel); |
|
return new ValueTask<string>(result); |
|
} |
|
|
|
public MyCompiledTemplate<MyHtmlTemplateBase<TViewModel>, TViewModel> GetCompiledHtmlTemplate<TViewModel>( |
|
string viewTemplateName, |
|
string viewTemplate, |
|
string? layoutTemplateName = null, |
|
string? layoutTemplate = null, |
|
IDictionary<string, string>? includes = null) |
|
{ |
|
var cacheKey = GetCacheKey(viewTemplateName, layoutTemplateName, includes); |
|
|
|
return (MyCompiledTemplate<MyHtmlTemplateBase<TViewModel>, TViewModel>) |
|
_htmlCache.GetOrAdd(cacheKey, key => |
|
{ |
|
includes = AddTemplateToIncludes(layoutTemplateName, layoutTemplate, includes); |
|
return _razorEngine.CompileHtml<TViewModel>(viewTemplate, includes); |
|
}); |
|
} |
|
|
|
public ValueTask<string> RunForNonHtmlAsync<TViewModel>( |
|
string viewTemplateName, |
|
string viewTemplate, |
|
TViewModel viewModel, |
|
string? layoutTemplateName = null, |
|
string? layoutTemplate = null, |
|
IDictionary<string, string>? includes = null) |
|
{ |
|
var compiledTemplate = GetCompiledTemplate<TViewModel>( |
|
viewTemplateName, |
|
viewTemplate, |
|
layoutTemplateName, |
|
layoutTemplate, |
|
includes); |
|
|
|
var result = compiledTemplate.Run(viewModel); |
|
return new ValueTask<string>(result); |
|
} |
|
|
|
public MyCompiledTemplate<MyTemplateBase<TViewModel>, TViewModel> GetCompiledTemplate<TViewModel>( |
|
string viewTemplateName, |
|
string viewTemplate, |
|
string? layoutTemplateName = null, |
|
string? layoutTemplate = null, |
|
IDictionary<string, string>? includes = null) |
|
{ |
|
var cacheKey = GetCacheKey(viewTemplateName, layoutTemplateName, includes); |
|
|
|
return (MyCompiledTemplate<MyTemplateBase<TViewModel>, TViewModel>) |
|
_nonHtmlCache.GetOrAdd(cacheKey, key => |
|
{ |
|
includes = AddTemplateToIncludes(layoutTemplateName, layoutTemplate, includes); |
|
return _razorEngine.CompileHtml<TViewModel>(viewTemplate, includes); |
|
}); |
|
} |
|
|
|
private static string GetCacheKey( |
|
string viewTemplateName, |
|
string? layoutTemplateName = null, |
|
IDictionary<string, string>? includes = null) |
|
{ |
|
return $"{layoutTemplateName ?? "NoLayout"}_" + |
|
$"{viewTemplateName}_" + |
|
$"{(includes == null ? "NoIncludes" : string.Join(",", includes.Select(i => i.Key)))}"; |
|
} |
|
|
|
private static IDictionary<string, string>? AddTemplateToIncludes( |
|
string? layoutTemplateName, |
|
string? layoutTemplate, |
|
IDictionary<string, string>? includes) |
|
{ |
|
// The engine requires the layout in the includes, but for convenience we make that easy for callers... |
|
// But now we need to include it |
|
if (layoutTemplate is not null) |
|
{ |
|
if (layoutTemplateName is null) |
|
{ |
|
throw new ArgumentNullException( |
|
nameof(layoutTemplateName), |
|
$"If a {nameof(layoutTemplate)} is provided, a {nameof(layoutTemplateName)} should be too."); |
|
} |
|
|
|
includes ??= new Dictionary<string, string>(1); |
|
includes.Add(layoutTemplateName, layoutTemplate); |
|
} |
|
|
|
return includes; |
|
} |
|
} |
|
} |