You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

是否存在可磁盘缓存并复用的RazorLight ICachingProvider现成实现?

解决方案

一、实现跨进程复用的文件系统缓存提供器

由于MemoryCachingProvider是进程内缓存,多次调用exe会失效,需要自定义实现ICachingProvider,将模板编译产物(避免直接缓存Assembly)持久化到磁盘:

public class FileSystemCachingProvider : ICachingProvider
{
    private readonly string _cacheDir;

    public FileSystemCachingProvider(string cacheDirectory)
    {
        _cacheDir = Path.GetFullPath(cacheDirectory);
        Directory.CreateDirectory(_cacheDir);
    }

    public bool Contains(string key)
    {
        var cachePath = GetSafeCachePath(key);
        return File.Exists(cachePath);
    }

    public void Insert(string key, ITemplatePage templatePage)
    {
        var cachePath = GetSafeCachePath(key);
        // 缓存Razor生成的C#代码而非Assembly,规避内存Assembly序列化问题
        var cachedData = new CachedTemplate
        {
            GeneratedCode = templatePage.GeneratedCode,
            ModelTypeFullName = templatePage.ModelType.AssemblyQualifiedName
        };

        File.WriteAllText(cachePath, JsonSerializer.Serialize(cachedData));
    }

    public ITemplatePage Retrieve(string key)
    {
        var cachePath = GetSafeCachePath(key);
        if (!File.Exists(cachePath)) return null;

        var cachedData = JsonSerializer.Deserialize<CachedTemplate>(File.ReadAllText(cachePath));
        var modelType = Type.GetType(cachedData.ModelTypeFullName);
        
        // 从缓存代码重新编译生成TemplatePage
        var compiler = new RazorTemplateCompiler();
        var compilationResult = compiler.CompileSource(cachedData.GeneratedCode, modelType);
        
        return compilationResult.CompiledTemplate;
    }

    private string GetSafeCachePath(string key)
    {
        // 将模板key转为安全文件名(用哈希避免非法字符)
        var hash = SHA256.HashData(Encoding.UTF8.GetBytes(key));
        var fileName = $"{Convert.ToHexString(hash)}.json";
        return Path.Combine(_cacheDir, fileName);
    }

    private class CachedTemplate
    {
        public string GeneratedCode { get; set; }
        public string ModelTypeFullName { get; set; }
    }
}

二、初始化RazorLight引擎

在exe工具中,使用自定义缓存提供器初始化引擎,确保多次调用复用缓存:

// 选择用户有权限的缓存目录,比如本地应用数据目录
var cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RazorTemplateCache");
var engine = new RazorLightEngineBuilder()
    .UseFileSystemProject("你的模板根目录")
    .UseCachingProvider(new FileSystemCachingProvider(cacheDir))
    .Build();

三、逐个传入模型的调用流程

  1. 首次调用:传入模板路径和第一个模型数据,引擎编译模板后将生成代码写入磁盘缓存,渲染输出结果。
  2. 后续调用:传入同一模板路径和新模型数据,引擎从磁盘读取缓存代码,重新编译生成TemplatePage后渲染。

PowerShell调用示例:

# 第一次调用,传入模型1
& .\RazorRenderer.exe -Template "Templates/UserProfile.cshtml" -Model '{"Name":"Alice","Age":30}'

# 后续调用,传入模型2
& .\RazorRenderer.exe -Template "Templates/UserProfile.cshtml" -Model '{"Name":"Bob","Age":25}'

四、内存Assembly的替代处理(可选)

若一定要缓存编译后的Assembly,可通过自定义编译服务将Assembly保存到磁盘,再在缓存提供器中复用:

public class FileSavingCompilationService : ICompilationService
{
    private readonly ICompilationService _innerService;
    private readonly string _assemblyDir;

    public FileSavingCompilationService(ICompilationService innerService, string assemblyDirectory)
    {
        _innerService = innerService;
        _assemblyDir = assemblyDirectory;
        Directory.CreateDirectory(_assemblyDir);
    }

    public CompilationResult Compile(CompilationOptions options)
    {
        var result = _innerService.Compile(options);
        if (result.CompiledAssembly != null)
        {
            // 将内存Assembly写入磁盘
            var assemblyPath = Path.Combine(_assemblyDir, $"{options.TemplateName}.dll");
            using var stream = new FileStream(assemblyPath, FileMode.Create);
            result.CompiledAssembly.Save(stream);
        }
        return result;
    }
}

初始化时替换编译服务:

var assemblyDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RazorTemplateAssemblies");
var engine = new RazorLightEngineBuilder()
    .UseFileSystemProject("你的模板根目录")
    .UseCompilationService(new FileSavingCompilationService(new DefaultCompilationService(), assemblyDir))
    .UseCachingProvider(new FileSystemCachingProvider(cacheDir))
    .Build();

关键注意事项

  • 模板key必须固定(比如用模板绝对路径),确保多次调用匹配同一缓存项。
  • 新增-ClearCache参数,用于模板更新时清理旧缓存。
  • 缓存目录需选择用户有读写权限的路径,避免权限问题。

内容的提问来源于stack exchange,提问作者mark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:15:47