RazorEngine un-cache compiled templates

后端 未结 3 1302
萌比男神i
萌比男神i 2021-02-07 15:02

Currently, I am using RazorEngine v2.1 as part of a background process that sends templated emails (thousands of them). To speed things up, the templates are compiled with their

相关标签:
3条回答
  • 2021-02-07 15:18

    I recently upgraded to the latest stable version of RazorEngine (3.6.1) and my strategy for purging the cache no longer worked due to all the changes. A lot has changed and the documentation for this project is not only out of date but written from an authors perspective making for a poor user experience.

    This is my current code for purging all cached templates using the 3.6.1.

    public static class TemplateManager
    {
        static IRazorEngineService Service { get; set; }
        static TemplateServiceConfiguration Configuration { get; set; }
    
        static TemplateManager()
        {
            Configuration = new TemplateServiceConfiguration()
            {
                // setting up our custom template manager so we map files on demand
                TemplateManager = new MyTemplateManager()
            };
            Service = RazorEngineService.Create(Configuration);
            Engine.Razor = Service;
        }
    
        /// <summary>
        /// Resets the cache.
        /// </summary>
        public static void ResetCache()
        {
            Configuration.CachingProvider = new RazorEngine.Templating.DefaultCachingProvider();
        }
    
        /// <summary>
        /// Compiles, caches and parses a template using RazorEngine.
        /// </summary>
        /// <param name="templateType">Type of the template.</param>
        /// <param name="anonymousType">Type of the anonymous object.</param>
        /// <param name="cachedEnabled">true to enabled caching; false otherwise</param>
        /// <returns></returns>
        public static string GetTemplate<T>(EmailTemplateType templateType, T anonymousType, bool cachedEnabled = true)
        {
            string templateName = templateType.ToString();
    
            if (cachedEnabled == false)
                ResetCache();
    
            // pre-compile, cache & parse the template
            return Engine.Razor.RunCompile(templateName, null, anonymousType);
        }
    }
    
    public enum EmailTemplateType
    {
        ForgotPassword,
        EmailVerification
    }
    
    public class MyTemplateManager : ITemplateManager
    {
        public ITemplateSource Resolve(ITemplateKey key)
        {
            string file = HttpContext.Current.Server.MapPath(string.Format("~/EmailTemplates/{0}.cshtml", key.Name));
            return new LoadedTemplateSource(System.IO.File.ReadAllText(file), file);
        }
    
        public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
        {
            return new NameOnlyTemplateKey(name, resolveType, context);
        }
    
        public void AddDynamic(ITemplateKey key, ITemplateSource source)
        {
            throw new NotImplementedException("dynamic templates are not supported!");
        }
    }
    

    This is an example usage of the code in Asp.Net MVC:

    var emailBody = TemplateManager.GetTemplate(EmailTemplateType.ForgotPassword, new
    {
        SiteUrl = Url.Action(MVC.Home.Index(), protocol: Request.Url.Scheme),
        SiteFriendlyName = SiteSettings.Instance.DomainName.FriendlyName,
        PasswordResetLink = Url.Action(MVC.Account.ActionNames.ResetPassword, MVC.Account.Name, new { userId = user.Id, code = code }, protocol: Request.Url.Scheme),
        NotRequestedUrl = Url.Action(MVC.Account.ActionNames.PasswordResetNotReqeuested, MVC.Account.Name, new { userId = user.Id, requesterIpAddress = WebUtils.GetClientIPAddress(), code = code }, protocol: Request.Url.Scheme)
    },
    /* this setting allows me to disable caching during development */
    !SiteSettings.Instance.EmailSettings.DebugEmailTemplates );
    
    // I could also have a button on an admin page that executed this code to manually reset the cache in production.
    TemplateManager.ResetCache();
    
    0 讨论(0)
  • 2021-02-07 15:20

    It seems that RazorEngine stores cache for compiled templates inside TemplateService instance. So you can recreate new instances of TemplateService from time to time to drop all cached templates.

    You can also consider using my own library which is based on RazorEngine and implements custom caching mechanism with expiration: http://www.nuget.org/packages/Essential.Templating.Razor

    0 讨论(0)
  • 2021-02-07 15:27

    Answering this because it still seems to be relevant for some people. (https://github.com/Antaris/RazorEngine/issues/232#issuecomment-128802285)

    For this particular process, which might run for months at a time without a restart, this could constitute a serious memory leak if all the previous versions of templates are still hanging around.

    When you change and recompile templates you have a memory leak, because you cannot unload loaded assemblies (which RazorEngine compiles and loads for you in the background).

    The only way to really free the memory is to reload the AppDomain or restart the process.

    The other answers seem to talk about newer versions which prevent memory leaks in the default configuration (to make you aware of the problem) and require some custom configuration to be able to recompile a key with another template code. Note that all the other answers will actually increase memory consumption!

    matthid, a RazorEngine contributor

    0 讨论(0)
提交回复
热议问题