本篇使用套件 : RazorEngine 【官方文件 】
遙想當年巷口寶之林還開著時,我這鄉野村夫每每需要套Email寄信格式以及一些罐頭訊息時,總是串一堆字串,既醜又難維護
1 2 3 4 5 6 7 8 9 public ActionResult Test (string name ) { var sb = new StringBuilder(); sb.AppendLine(string .Format(@"Hello {0}, welcome to RazorEngine!" ,name)); return Content(sb.ToString()); }
如果碰到麻煩一點的,像是什麼狀況要套表頭,某某狀況又要移除Footer,那整個字串的邏輯東拼西湊,最後不執行根本難以看出結果會變成如何,往往就變成維護的黑洞跟死角誰接手誰倒楣 。
自從我認識了RazorEngine這套件後,世界就變美好了,只要你會套.Net MVC的View,那基本上這個套件絕對是適合你的神兵利器,廢話不多說,我們先來將剛剛版本改成用RazorEngine處理
先去Nuget安裝套件
[![](https://3.bp.blogspot.com/-C5_gHPnJWEc/Wl64J_9mh5I/AAAAAAAAISA/GiHxPzZP9Qs9OyY_eVI4FrxHeD7MELXxQCLcBGAs/s400/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-C5_gHPnJWEc/Wl64J_9mh5I/AAAAAAAAISA/GiHxPzZP9Qs9OyY_eVI4FrxHeD7MELXxQCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
套上RazorEngine後,可以改成這樣
1 2 3 4 5 6 7 8 9 10 11 12 13 public ActionResult TestRazorEngine (string name ) { string template = "Hello @Model.Name, welcome to RazorEngine!" ; var Result = Engine.Razor.RunCompile( templateSource: template, name: "HelloWorldTemplateKey" , modelType: null , model: new { Name = name }); return Content(Result); }
這邊解說一下程式碼,RazorEnginr提供了幾種方法來產生編譯過的Template,分別是
**Run **: 依據帶入的Name去尋找Cache中是否有編譯過的Template,如果Cache沒有這個Name的模板,則會跳Exception
**Compile **: 將帶入的模板內容編譯,並存進Cache
**RunCompile **: 等於上面兩者個結合,先去Cache中找看看有無這個Name的模板,有則直接回傳,沒有則先編譯後存入Cache,並回傳
依據官方文件所提到的,Compile Template是很耗效能的,所以建議都要存入Cache,避免每次去Compile(預設行為就是如此,當然之後也會提到如何改寫)
有了初步的認識後,應該會發現目前的做法跟拼湊字串一樣,如果只是這樣也就失去了使用RazorEngine的意義了,所以下一步是將這個Template內容變成.cshtml,可以大大的增加可讀性跟維護姓
[![](https://3.bp.blogspot.com/-pfz4iweMebY/Wl68R2ISmxI/AAAAAAAAISM/aEjP8lgBlY0krsOinpRLB1Q88251KecqQCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-pfz4iweMebY/Wl68R2ISmxI/AAAAAAAAISM/aEjP8lgBlY0krsOinpRLB1Q88251KecqQCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) 新增_HelloWorld.cshtml Template
將剛剛的字串貼進去,並且就像我們一般套版一樣,建立對應的ViewModel
[![](https://2.bp.blogspot.com/-cEGoNuCIBPc/Wl68rMWc_6I/AAAAAAAAISQ/wECO1yhBn2My81LPW3lK9aKo9QfXMHnOACLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://2.bp.blogspot.com/-cEGoNuCIBPc/Wl68rMWc_6I/AAAAAAAAISQ/wECO1yhBn2My81LPW3lK9aKo9QfXMHnOACLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
1 2 3 4 5 public class HellowWorldRazorModel { public string Name { get ; set ; } }
原本的程式碼改寫成如下
1 2 3 4 5 6 7 8 9 10 public ActionResult TestRazorEngine (string name ) { var Result = Engine.Razor.RunCompile( "_HelloWorld" , typeof (HellowWorldRazorModel), new HellowWorldRazorModel { Name = name }); return Content(Result); }
但執行之後就會出Exception,原因是”_HelloWorld”這個Template Name它不知道怎麼對應到哪個cshtml模板
[![](https://4.bp.blogspot.com/-8LQ2IYJBnmI/Wl6-F94TFFI/AAAAAAAAISg/FuId2F7jKVYBpu3wOMLY1UPSkOY2P5E2QCLcBGAs/s640/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://4.bp.blogspot.com/-8LQ2IYJBnmI/Wl6-F94TFFI/AAAAAAAAISg/FuId2F7jKVYBpu3wOMLY1UPSkOY2P5E2QCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
所以我們要自己寫RazorEngine的ITemplateManager : 官方文件參考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class MyTemplateManager : ITemplateManager { public ITemplateSource Resolve (ITemplateKey key ) { var template = Tools.ChkCache(key.Name) as string ; if (string .IsNullOrWhiteSpace(template)) { var FilePath = string .Format("~/Views/RazorTemplate/{0}.cshtml" , key.Name); template = File.ReadAllText(HttpContext.Current.Server.MapPath(FilePath)); Tools.SaveToCache(key.Name, template, 3600 , CacheType.Absolute, null ); } return new LoadedTemplateSource(template, null ); } 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!" ); } }
這邊我只改寫Resolve這個方法,其他都沿用官方文件的範本,內容只要是依據帶入的Name去確認Cache是否已經有Template(因為用公司的底層套件,所以確認Cache跟Save Cache可能跟一般認知的方法不同,請忽略或用.Net提供的Cache方法即可 ),如果沒有快取,則依據帶入的Name去對應的Folder位置讀取.cshtml並且Cache,然後透過RazorEngine提供的方法LoadedTemplateSource回傳TemplateSource。
這一段可以讓RazorEngine知道如何將Name對應到我們的cshtml,寫完客製的MyTemplateManager後接著是設定
Global.asax加上這段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected void Application_Start ( ) { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); var config = new TemplateServiceConfiguration(); config.TemplateManager = new MyTemplateManager(); var service = RazorEngineService.Create(config); Engine.Razor = service; }
接著剛剛的程式就能執行了!!
[![](https://3.bp.blogspot.com/-NWxWGypeAdo/Wl7AVFb9jRI/AAAAAAAAISs/24bA9qjOkTwoY1ca8SDc8_OXhuB3vI_2QCLcBGAs/s640/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-NWxWGypeAdo/Wl7AVFb9jRI/AAAAAAAAISs/24bA9qjOkTwoY1ca8SDc8_OXhuB3vI_2QCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
RazorEngine也提供Layout的套版方法,可以在cshmtl上面設定對應的Layout,不用像先前提到的,拼字串拼得亂七八糟了
[![](https://3.bp.blogspot.com/-lOts6puSj9Q/Wl7BYAAbwJI/AAAAAAAAIS0/E05MfohLLpgG0Cp2x63uJ8i6ZL-ifJdagCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-lOts6puSj9Q/Wl7BYAAbwJI/AAAAAAAAIS0/E05MfohLLpgG0Cp2x63uJ8i6ZL-ifJdagCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) 新增_OurLayout.cshmtl
[![](https://3.bp.blogspot.com/-e3Q9NSlCVuk/Wl7Bsff9odI/AAAAAAAAIS4/J0XN1F7PPW0rqsRmvA9yw5cGcTeAjJiawCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-e3Q9NSlCVuk/Wl7Bsff9odI/AAAAAAAAIS4/J0XN1F7PPW0rqsRmvA9yw5cGcTeAjJiawCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) Layout的內容
[![](https://3.bp.blogspot.com/-ugzdMhXnb3w/Wl7CFh0QMAI/AAAAAAAAITA/yxDlQmxK1f4X_4zjamWyCEJnoTUWgwfkgCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-ugzdMhXnb3w/Wl7CFh0QMAI/AAAAAAAAITA/yxDlQmxK1f4X_4zjamWyCEJnoTUWgwfkgCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
執行結果
[![](https://1.bp.blogspot.com/-t8lTrthvFXQ/Wl7CXvDYDMI/AAAAAAAAITE/k4aTtPG7zsIUmvUG4U5hvI7nyOpV6JzmACLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://1.bp.blogspot.com/-t8lTrthvFXQ/Wl7CXvDYDMI/AAAAAAAAITE/k4aTtPG7zsIUmvUG4U5hvI7nyOpV6JzmACLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
RazorEngine PartialView的用法
在一般套版,我們可能會把共用的區塊挖成PartialView重複使用,但這邊的用法比較特別,須改用Include這個方法取代
[![](https://4.bp.blogspot.com/-m5NwrfUZcMs/Wl7C11d-lnI/AAAAAAAAITM/AA-QS-UbaEgFXr8MUjx3olooVMgAEkLbwCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://4.bp.blogspot.com/-m5NwrfUZcMs/Wl7C11d-lnI/AAAAAAAAITM/AA-QS-UbaEgFXr8MUjx3olooVMgAEkLbwCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) 新增PartialView
[![](https://1.bp.blogspot.com/-NvEYUWh1nkw/Wl7DBfTkNOI/AAAAAAAAITQ/SBSCU6uWL2ck3U7nncXsEo4q5mdRFboiQCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://1.bp.blogspot.com/-NvEYUWh1nkw/Wl7DBfTkNOI/AAAAAAAAITQ/SBSCU6uWL2ck3U7nncXsEo4q5mdRFboiQCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) PartialView的內容,而且也可以使用ViewBag傳遞參數
[![](https://1.bp.blogspot.com/-Ll9MHoSaYtM/Wl7Dk_WcOGI/AAAAAAAAITY/8CF1z0SXs2M21_tS3YCH5KrEY6WP6zAAgCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://1.bp.blogspot.com/-Ll9MHoSaYtM/Wl7Dk_WcOGI/AAAAAAAAITY/8CF1z0SXs2M21_tS3YCH5KrEY6WP6zAAgCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png) 改寫原本的_HelloWorld.cshtml,套入PartialView
執行結果!!
[![](https://3.bp.blogspot.com/-osb6w9vTl8o/Wl7D2hzXn2I/AAAAAAAAITc/KpzyaZf08Fseqa10QxbEEaIdKAJDELv6gCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)](https://3.bp.blogspot.com/-osb6w9vTl8o/Wl7D2hzXn2I/AAAAAAAAITc/KpzyaZf08Fseqa10QxbEEaIdKAJDELv6gCLcBGAs/s1600/%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D.png)
希望對大家有幫助~