0%

【RazorEngine】套寄信、罐頭訊息內容的好幫手

本篇使用套件 : 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)
{
// Resolve your template here (ie read from disk)
// if the same templates are often read from disk you propably want to do some caching here.
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);
}
// Provide a non-null file to improve debugging
return new LoadedTemplateSource(template, null);
}

public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
{
// If you can have different templates with the same name depending on the
// context or the resolveType you need your own implementation here!
// Otherwise you can just use NameOnlyTemplateKey.
return new NameOnlyTemplateKey(name, resolveType, context);
// template is specified by full path
//return new FullPathTemplateKey(name, fullPath, resolveType, context);
}

public void AddDynamic(ITemplateKey key, ITemplateSource source)
{
// You can disable dynamic templates completely.
// This just means all convenience methods (Compile and RunCompile) with
// a TemplateSource will no longer work (they are not really needed anyway).
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);

//RazorEngine Setting
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)

希望對大家有幫助~