toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.52k stars 259 forks source link

The CompileRenderStringAsync still caching templates #518

Closed alexey62soft closed 1 year ago

alexey62soft commented 1 year ago

Hi everyone!

It seems in RazorLight I can't completely disable caching.

I try to use the following configuration:

services.AddRazorLight(() => new RazorLightEngineBuilder().UseNoProject().Build());

and render approach (fetching template from datastore):

var result = _razorEngine.CompileRenderStringAsync("type", "template", new());

After some investigation I found the EngineHandler that relies on RazorTemplateCompiler has an uncontrolled "L0" cache: https://github.com/toddams/RazorLight/blob/master/src/RazorLight/Compilation/RazorTemplateCompiler.cs#L46

On another side, EngineHandler has an instance of RazorLightOptions with DynamicTemplates property which stores the actual version of the template. But, due to having an "L0" Compiler cache, it still returns an obsolete result.

To Reproduce

All steps and explanations - are above.

Expected behavior

I expect to receive the actual version of the template and don't have it cached in the compiler.

Information (please complete the following information):

Additional context Add any other context about the problem here.

alexey62soft commented 1 year ago

@jzabroski , can you comment on that, please?

jzabroski commented 1 year ago

It sounds like you want to use scoped services instead of registering things as a singleton.

The L0 Cache is not a bug, and designed that way on purpose. It is possible you would want to update the template, but that is not a typical scenario. Thus, the default registration methods provided in-box make the most sense.

alexey62soft commented 1 year ago

@jzabroski, yes, it seems I need to use a scoped approach, but, unfortunately, the library has no implemented IDisposable interface which causes OOM issues with the scoped approach.

jzabroski commented 1 year ago

What would you propose?

alexey62soft commented 1 year ago

I would propose 2 variants:

  1. Implement an IDisposable interface for IRazorViewEngine and all its custom dependencies to dispose the service properly via DI;
  2. (Simpler) - add optional configuration for "L0" caching to allow configure retention policy. This will allow the usage of Singleton and refresh templates if necessary.
alexey62soft commented 1 year ago

Closing issue after deep investigation of the compilation process.

jzabroski commented 1 year ago

What was your conclusion?

ruffin-- commented 1 month ago

What did happen here? I'm designing a razor template and wanted to save cshtml, re-render, and see updates with inserted values to see the new changes. I'm not quickly seeing an example of how to invalidate the cache and run into this issue.

I was initially hoping just removing .UseMemoryCachingProvider() would do it, but it appears from the above that you'll still get some default caching. ?? (That's certainly what I'm seeing.)

        var engine = new RazorLightEngineBuilder()
            .UseEmbeddedResourcesProject(Assembly.GetAssembly(typeof(ReportServices)))
            //.UseMemoryCachingProvider()
            .Build();

// ...

        var htmlEmail = await engine.CompileRenderAsync(uniquePathToTemplate, myModel, null);

(I'm running the above multiple times.)