最近剛好有要寫寄Email的程式,在代碼中寫HTML覺得很呆,抽出代碼外寫到txt或html檔當範本,由程式執行時在載入檔案時用Regex換關鍵字又覺得不夠好用,而且因為有時會有要判斷一些條件,就會寫一堆if esle在代碼中看了就討厭,因為寫MVC久了,就很希望範本也可以像MVC中的View,傳Model過去,在View層決定如何呈現,而更希望是使用Razor語法來寫範本,花了時間研究,找到RazorEngine,使用它來載入檔案,由它來編譯與執行並輸入結果。

RazorEngine

官網網址:http://razorengine.codeplex.com/

在找到RazorEngine之前曾經想過其他的方案,如T4與V8 Engine載jquery.template,但T4如果要獨立於MSBuild或Visual Studio執行有點麻煩,而V8 Engine我又不想在Class Library專案中放一堆js檔,後來就想到Razor,因為Razor的相關處理都是寫在System.Web.Razor,雖然Namespace叫System.Web,但根本沒有載入任何的System.Web相關的組件(如圖一),所以我肯定它可以獨立在非Web環境於中使用,在研究如何運用的期間,就發現早在去年就有人寫好放在CodePlex上,我太落伍了。

圖一 System.Web.Razor的參考,只有載入基本的三個組件

註:有興趣知道如何使用System.Web.Razor產生結果(基本上都是CodeDom),我所知道就有四套,小弟這一篇[ASP.NET MVC]Razor Views 預編譯(Pre-Compile)[1]-加快第一次執行回應速度介紹的二個Extension與本篇的RazorEngine與ASP.NET MVC 3使用的System.Web.WebPages.Razor,除了System.Web.WebPages.Razor外都是Open Source的可以下載Code來觀摩。

下面的範例,將展示如果只單獨使用System.Web.Razor.dll,如何產生結果(有點累,還是用別人的組件比較好)。

01 namespace RezorCodeDomSample
02 {
03     internal class Program
04     {
05         private static void Main(string[] args)
06         {
07             //簡單的範本
08             string template = @"@{var name=""Would"";}
09                                 Hello @name!!";
10  
11             var input = new System.IO.StringReader(template);
12  
13             //產生Razor的TemplateEngine
14             var host = new RazorEngineHost(new CSharpRazorCodeLanguage());
15             host.DefaultBaseClass = "RezorCodeDomSample.MyTemplate";
16             host.DefaultNamespace = "RezorCodeDomSample";
17             host.DefaultClassName = "MyTemplateResult";
18             var engine = new RazorTemplateEngine(host);
19  
20             //取得結果的CodeDom
21             var code = engine.GenerateCode(input);
22             var codeType = code.GeneratedCode.Namespaces[0].Types[0];
23             var codeProvider = new CSharpCodeProvider();
24  
25             //將CodeDom輸出到檔案中
26             //CodeGeneratorOptions options = new CodeGeneratorOptions();
27             //options.BlankLinesBetweenMembers = true;
28             //System.IO.StringWriter sw = new System.IO.StringWriter();
29             //codeProvider.GenerateCodeFromCompileUnit(code.GeneratedCode, sw, options);
30             //File.WriteAllText("c:\\text.cs", sw.ToString());
31  
32             //將CodeDom編譯
33             var options = new CompilerParameters()
34             {
35                 GenerateInMemory = true,
36                 GenerateExecutable = false,
37             };
38             options.ReferencedAssemblies.Add(typeof(Program).Assembly.Location);
39             var asselby = codeProvider.CompileAssemblyFromDom(options, code.GeneratedCode);
40  
41             //執行Template
42             var type = asselby.CompiledAssembly.GetType("RezorCodeDomSample.MyTemplateResult");
43             var ins = Activator.CreateInstance(type) as MyTemplate;
44             ins.Execute();
45             Console.Write(ins.Reault);
46         }
47     }
48  
49     //如果沒有Base類會不好處理
50     public class MyTemplate
51     {
52         private StringBuilder sb = new StringBuilder();
53  
54         public virtual void Execute()
55         {
56         }
57  
58         public void Write(object value)
59         {
60             sb.Append(value);
61         }
62  
63         public void WriteLiteral(object value)
64         {
65             sb.Append(value);
66         }
67  
68         public string Reault
69         {
70             get return sb.ToString(); }
71         }
72     }
73 }

使用範例(部份直接使用官網的範例)

一般用法

1 string template = "Hello @Model.Name! Welcome to Razor!";
2 string result = Razor.Parse(template, new { Name = "World" }, "Sample");

最後一個參數Name是選項參數,但建議給值因為關係到快取,如果有給,下次使用相同名稱的範本會用快取的,而且關係到範本的Include,雖然RazorEngine不能用RanderAction或RanderPartial但有提供Include可以載入,也是使用此Name為關鍵字。

使用.cshtml檔案

只要是副檔名為.cshtml,就算不在ASP.NET MVC3專案中,編輯.cshtml的方式都相同(但缺web.config中的設定,所以有些功能出不來),當然範本檔副檔名不一定要為.cshtml,但有IDE支援總比沒有好。

只是寫法要變,因為ASP.NET MVC 3的BaseType是System.Web.Mvc.WebViewPage,而RazorEngine的BaseType是RazorEngine.Templating.TemplateBase,除了Model屬性外其他的Html、Url、Ajax等等屬性都不沒有,但是C#的語法都支援。

1 string result = Razor.Parse(File.ReadAllText("test.cshtml"), new { IsVip = true, Name = "Wade" });

進階用法

Template的Include

RazorEngine雖然不支援RanderAction或RanderPartial,不過他有提供Include方法,載入已經Compile(或Parse)過的範本,以下是使用範例:

1 string template1 = "Hello @Model.Name";
2 string template2 = "This is my sample template, @Include(\"Template1\",Model)";
3 Razor.Compile(template1, "Template1");
4 string result = Razor.Parse(template2, new { Name = "Wade" });

內嵌方法

如果只有單一個範本會用到的方法可以使用內嵌方法,以下是使用範例:

1 string template = @"
2 @helper MyMethod(string name) {
3   Hello @name
4 }
5  
6 @MyMethod(Model.Name)! Welcome to Razor!";
7  
8 string result = Razor.Parse(template, new { Name = "World" });

BaseType

每一個範本,最後都會使用System.Web.Razor.RazorTemplateEngine編譯成Class,而且繼承BaseType,所以BaseType決定了範本能使用的功能,如果還是不清楚以第一個範例為例:

1 @"@{var name=""Would"";}
2   Hello @name!!";

這些內容經剖析後會變成這樣

01 //與第一個範例拿掉註解後所產生的test.cs檔案相同
02 namespace RezorCodeDomSample
03 {
04     public class MyTemplateRsult : RezorCodeDomSample.MyTemplate
05     {
06         public MyTemplateRsult()
07         {
08         }
09  
10         public override void Execute()
11         {
12             var name = "Would";
13  
14             WriteLiteral("Hello ");
15  
16  
17             Write(name);
18             WriteLiteral("!!\r\n");
19         }
20     }
21 }

這樣有比較清楚BaseType的功用了嗎?

所以如果希望所有的範本都可以使用的功能,可以繼承RazorEngine.Templating.TemplateBase,將擴充功能寫在子類別,然後註冊子類別,以下是使用範例:

01 public abstract class MyCustomTemplateBase<T> : TemplateBase<T>
02 {
03   public string ToUpperCase(string name)
04   {
05     return name.ToUpperCase();
06   }
07 }
08  
09 //註冊子類
10 Razor.SetTemplateBase(typeof(MyCustomTemplateBase<>));
11  
12 string template = "My name in UPPER CASE is: @ToUpperCase(Model.Name)";
13 string result = Razor.Parse(template, new { Name = "Matt" });

載入組件與命名空間

RazorEngine註冊CodeDom所使用的組件是使用AppDomain.CurrentDomain.GetAssemblies()的方式,所以只要在專案中所參考的組件,範本中都可以使用,還有為了增加撰寫的便利性,可以增加命名空間,以下是使用範例:

1 Razor.DefaultTemplateService.Namespaces.Add("System.Xml");
2  
3 string template = @"@{
4      var xml = new XmlDocument();
5      xml.LoadXml(Model);
6      Write(xml.InnerXml);
7 }";
8 string result = Razor.Parse<string>(template, "<Test>test</Test>");

設定檔

跟ASP.NET MVC 3一樣,命名空間或BaseType等等都可以寫在設定檔中,詳情請參考官網

增加區段

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <configuration>
3     <configSections>
4         <section name="razorEngine" type="RazorEngine.Configuration.RazorEngineConfigurationSection, RazorEngine" requirePermission="false" />
5     </configSections>
6 </configuration>

增加設定

01 <razorEngine activator="RazorEngineSamples.Activators.MySampleActivator, RazorEngineSamples"
02              factory="RazorEngine.Web.WebCompilerServiceFactory, RazorEngine.Web">
03     <namespaces>
04         <add namespace="System.Linq" />
05     </namespaces>
06     <templateServices default="myCustomTemplateService">
07         <add name="myCustomTemplateService1" language="CSharp" />
08         <add name="myCustomTemplateService2" templateBase="MyTemplateBase"/>
09     </templateServices>
10 </razorEngine>
1   

TemplateService可以讓某些範本使用不同的設定,以下是使用範例:

1 var service = Razor.Services["myCustomTemplateService"];
2 string result = service.Parse("Hello @Model.Name"new { Name = "World" });

用Razor語法寫範本-RazorEngine組件介紹的更多相关文章

  1. 用Razor語法寫範本-RazorEngine組件介紹【转——非常好,可以用它来代替NVelocity】

    RazorEngine 官網網址:http://razorengine.codeplex.com 在找到RazorEngine之前曾經想過其他的方案,如T4與V8 Engine載jquery.temp ...

  2. 開賣!下集 -- ASP.NET 4.5 專題實務(II)-範例應用與 4.5新功能【VB/C# 雙語法】

    開賣!下集 -- ASP.NET 4.5 專題實務(II)-範例應用與 4.5新功能[VB/C# 雙語法] 我.....作者都沒拿到書呢! 全台灣最專業的電腦書店 -- 天瓏書局 已經開賣了! 感謝天 ...

  3. Java學習筆記(基本語法)

    本文件是以學習筆記的概念為基礎,用於自我的複習紀錄,不過也開放各位的概念指證.畢竟學習過程中難免會出現觀念錯誤的問題.也感謝各位的觀念指證. 安裝JDK 在Oracle網站中找自己系統的JDK下載位置 ...

  4. vue起步和模板語法

    vue構造器里有哪些內容: var a=new Vue({}) 模板語法: 文本插值: html: html的屬性里的值應該使用v-bind: 表達式: 指令; 參數:v-bind,v-on 雙向用戶 ...

  5. 查看LINQ Expression編譯後的SQL語法(转)

    在用了LINQ語法之後的一個月,我幾乎把SQL語法全部拋到腦後了,不過 LINQ好用歸好用,但是實際上操作資料庫的還是SQL語法,如果不知道LINQ語法 編譯過後產生怎樣的SQL語法,一不小心效能就會 ...

  6. Delphi APP 開發入門(六)Object Pascal 語法初探

    Delphi APP 開發入門(六)Object Pascal 語法初探 分享: Share on facebookShare on twitterShare on google_plusone_sh ...

  7. GO語言基礎教程:數組,切片,map

    這節課我們來講解數組,切片和map,或許您是從其他語言轉到GO語言這邊的,那麼在其他語言的影響下您可能會不太適應GO語言的數組,因為GO語言把數組給拆分成了array,slice和map,接下來的時間 ...

  8. 何解決 LinqToExcel 發生「無法載入檔案或組件」問題何解決 LinqToExcel 發生「無法載入檔案或組件」問題

    在自己的主機上透過 Visual Studio 2013 與 IISExpress 開發與測試都還正常,但只要部署到測試機或正式機,就是沒辦法順利執行,卡關許久之後找我協助.我發現錯誤訊息確實很「一般 ...

  9. jquery 語法

    基本形式: $(selector).action() 文檔加載函數: $(document).Ready{ function(){ //將所有的函數寫到文檔加載函數里,可以防止頁面未加載完全,就執行j ...

随机推荐

  1. C#并行编程(4):基于任务的并行

    C#中的任务Task 在C#编程中,实现并行可以直接使用线程,但使用起来很繁琐:也可以使用线程池,线程池很大程度上简化了线程的使用,但是也有着一些局限,比如我们不知道作业什么时候完成,也取不到作业的返 ...

  2. 异常日志框架Exceptionless结合.NET Core(本地部署)

    一.前言 1.分布式异常日志收集框架Exceptionless是开源的工具,根据官方给出的说明: Exceptionless可以为您的ASP.NET.Web API.WebFrm.WPF.控制台和MV ...

  3. Spring重复扫描导致事务失败的解决方案及深入分析

    问题及日志使用Spring和mybatis,然后配置事务,出现SqlSession was not registered for synchronization because synchroniza ...

  4. ios 从工程中删除Cocoapods

    删除工程文件夹下的Podfile.Podfile.lock及Pods文件夹 2. 删除xcworkspace文件 3. 使用xcodeproj文件打开工程,删除Frameworks组下的Pods.xc ...

  5. 关于使用react的思考

    1. 组件化开发:将可以复用的部分独立封装成一个组件,每个部分的数据互不影响

  6. MongoDb GridFS的使用

    MongoDb GridFS 是MongoDB的文件存储方案,主要用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片.音频等),对大文件有着更好的性能. 要在C#中使用GridFS,首先 ...

  7. JAVA 对象序列化(二)——Externalizable

    Java默认的序列化机制非常简单,而且序列化后的对象不需要再次调用构造器重新生成,但是在实际中,我们可以会希望对象的某一部分不需要被序列化,或者说一个对象被还原之后,其内部的某些子对象需要重新创建,从 ...

  8. [Winform]在关闭程序后,托盘不会消失的问题

    摘要 在开发winform程序时,添加了系统托盘,然发现在程序关闭后,托盘并不会消失,鼠标放在上面之后,才会消失.猜测是资源没有释放干净引起的.托盘作为form的组件,应该会随着form的关闭而释放啊 ...

  9. 委托、Lambda表达式、事件系列07,使用EventHandler委托

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

  10. [转发]将Delphi的对象方法设为回调函数

    心血来潮,为了实现更好的通用性和封装性,需要把类方法作为回调函数,搜得一篇好文,节选转发.命名似乎应该是MethodToCallback才合适,可惜调试时总是报错,debugging. 原文地址:ht ...