2013-01-16 63 views
9

目前,我使用RazorEngine v2.1作爲發送模板化電子郵件(數以千計)的後臺進程的一部分。爲了加快速度,模板以md5和作爲名稱進行編譯。這樣可以在模板更改時重新編譯,並且使用該模板的所有電子郵件都可以使用相同的編譯模板。我跟蹤列表中已編譯模板的名稱,以便我知道何時再次調用編譯(以及執行其他一些操作)。RazorEngine un-cache編譯模板

問題:它發生,我認爲經過一個很長的時間和大量的模板修改,所有這些緩存編譯模板仍然可能會在內存中,因爲它看起來像他們被存儲在一個dynamic之後。對於這個特定的進程,可能每次運行幾個月而沒有重新啓動,如果所有先前版本的模板仍然處於閒置狀態,則可能會造成嚴重的內存泄漏。

問題:是否有解除舊緩存模板的方法,以便它們不再在dynamic中掛起?

例如,如果我能夠自己保留已編譯的模板對象,並在我想使用它們時將它們傳遞到RazorEngine中,我可以決定何時拋出它們並消除內存泄漏。但是,如果RazorEngine已經有了解決這個問題的方法,那麼知道這個方法也很方便,因爲我無法在互聯網上找到對這個特定問題的很多參考。關於爲什麼要使用編譯模板來減少內存使用量,有很多事情要做,但我很難找到任何有關噸和大量未使用的編譯模板累積在長壽命應用程序中的內容。

編輯:我剛剛讀了一下關於如何緩存的作品,並且如果同一個名字被傳入一個不同的模板它會重新緩存它,並放棄舊的。然而,這個問題仍然存在,因爲隨着時間的推移,電子郵件將被添加和刪除,並且隨着時間的推移,所有舊的被刪除的電子郵件仍將存在(即使它不會存儲每個版本模板的副本)。

回答

2

似乎RazorEngine在TemplateService實例中存儲編譯模板的緩存。因此,您可以不時重新創建TemplateService的新實例,以刪除所有緩存的模板。

您也可以考慮使用一種基於RazorEngine並實現自定義的緩存機制到期我自己的圖書館:http://www.nuget.org/packages/Essential.Templating.Razor

4

我最近升級到RazorEngine(3.6.1)的最新穩定版本,我的用於清除策略由於所有更改,緩存不再有效。很多內容已經改變,這個項目的文檔不僅過時了,而且從作者的角度出發,造成了糟糕的用戶體驗。

這是我使用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!"); 
    } 
} 

這是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

我的情況有點不同,我想模板(emailBody)返回從控制器用戶所以它會在頁面上可見。我怎樣才能將它作爲視圖返回? – user2818430

+0

@tugboatcaptain我試過你的解決方案,並得到錯誤'在RazorEngine.dll中發生類型'System.ArgumentException'的異常,但沒有在用戶代碼中處理。附加信息:模擬的無效標記 - 不能重複。我所做的就是從數據庫中獲取模板。 – dev

+0

很好的回答。 Thx的幫助! –

5

回答這個,因爲它似乎仍然是相關的一些人。 (https://github.com/Antaris/RazorEngine/issues/232#issuecomment-128802285

對於這個特殊的過程,這可能好幾個月的時間沒有重新啓動運行,這可能會構成嚴重的內存泄漏,如果所有的模板以前的版本仍在遊逛。

當您更改並重新編譯模板時,由於無法卸載加載的程序集(RazorEngine在後臺爲您編譯和加載),因此存在內存泄漏。

確實釋放內存的唯一方法是重新加載AppDomain或重新啓動進程。

其他答案似乎談論較新的版本,它可以防止默認配置中的內存泄漏(讓您意識到問題),並且需要一些自定義配置才能使用另一個模板代碼重新編譯密鑰。請注意,所有其他答案將實際上增加內存消耗!

matthid, 一個RazorEngine貢獻者