代碼生成利器-NCodeGenerate 教程(8) 揭開Razor模板引擎的神秘面紗


  NCodeGenerate使用的模板是Razor。了解一下Razor模板的生成原理,對調試NCodeGenerate 很有必要。下面講解一下。

一、模板基類

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.IO;
 6 namespace RazorDemo
 7 {
 8     public abstract class TemplateBase : IDisposable
 9     {
10         public TemplateBase()
11         {
12             this.Output = new StringWriter();
13         }
14         
15         public dynamic Model { get; set; }
16         public StringWriter Output { get; private set; }
17 
18         //寫入變量
19         public virtual void Write(object value)
20         {
21             this.WriteLiteral(value.ToString());
22         }
23 
24         //寫入固定字符串
25         public virtual void WriteLiteral(string value)
26         {
27             this.Output.Write(value);
28         }
29 
30         //入口函數
31         public abstract void Execute();
32 
33         void IDisposable.Dispose()
34         {
35             this.Output.Dispose();
36         }
37     }
38 }

這個類是所有模板的基類,Model 定義了一個 dynamic 類型的模型,用來從外部傳參數。Write(object value)  方法是在模板執行時,把對象的值寫入輸出,WriteLiteral(string value) 方法是把非C#代碼內容寫入到輸出。 在Razor 模板中的內容主要分為三類 :1、C#代碼 2、需要輸出C#變量 3、 非C# 的內容。

簡單的來說(不是很嚴謹),模板在解析時,對於第一類 C#代碼,會原封不動的輸出,對於第二類 會產生一條 Write(object value)的調用語句,對於第三類 會產生 一條 WriteLiteral(string value) 的調用語句。

public abstract void Execute(); 是一個虛的方法,在產生C#模板類中,會生成一個繼承自模板基類的子類,在子類中override 該方法,並把以上所產生的語句置於該方法之內。

這樣講起來可能不是很清楚。舉個例子,模板代碼如下:

 

1 @Model.Text
2 @for(int i=0;i<10;i++)
3 {
4     @:行數 @i.ToString()
5 }

在解析后產生的代碼如下:

 1 //------------------------------------------------------------------------------
 2 // <auto-generated>
 3 //     此代碼由工具生成。
 4 //     運行時版本:4.0.30319.296
 5 //
 6 //     對此文件的更改可能會導致不正確的行為,並且如果
 7 //     重新生成代碼,這些更改將會丟失。
 8 // </auto-generated>
 9 //------------------------------------------------------------------------------
10 
11 namespace _RazorDemo
12 {
13     using System;
14 
15 
16     public class _0bb7e1a8_fc50_4d4d_99b0_53d2e3adb560 : RazorDemo.TemplateBase
17     {
18 
19 #line hidden
20 
21 
22         public _0bb7e1a8_fc50_4d4d_99b0_53d2e3adb560()
23         {
24         }
25 
26         public override void Execute()
27         {
28 
29             Write(Model.Text);
30 
31             WriteLiteral("\r\n");
32 
33 
34             for (int i = 0; i < 10; i++)
35             {
36 
37                 WriteLiteral("    ");
38 
39                 WriteLiteral("行數 ");
40 
41 
42                 Write(i.ToString());
43 
44                 WriteLiteral("\r\n");
45 
46 
47             }
48 
49             WriteLiteral("\r\n");
50 
51 
52         }
53     }
54 }

看了產生的代碼應該就比較直觀了。

第二、模板解析

  模板解析主要是通過 RazorEngineHost 類和RazorTemplateEngine 類來實現的。看個例子吧

 1  public GeneratorResults ParseToCode(string TemplateCode,string defaultnamespace,string defaultclassname)
 2         {
 3             GeneratorResults razorResults;
 4             var host = new RazorEngineHost(new CSharpRazorCodeLanguage());
 5             host.DefaultBaseClass = typeof(TemplateBase).FullName;
 6             host.DefaultNamespace =defaultnamespace;
 7             host.DefaultClassName = defaultclassname;
 8             host.NamespaceImports.Add("System");
 9             host.GeneratedClassContext = new GeneratedClassContext("Execute","Write", "WriteLiteral");
10         
11             
12             var engine = new RazorTemplateEngine(host);
13             using (var reader = new StringReader(TemplateCode))
14             {
15                 razorResults = engine.GenerateCode(reader);
16                 
17                 CSharpCodeProvider codeProvider = new CSharpCodeProvider();
18                 CodeGeneratorOptions options = new CodeGeneratorOptions();
19                 options.BracingStyle = "C";
20                
21                
22                 using (StringWriter writer = new StringWriter())
23                 {
24                     IndentedTextWriter indentwriter = new IndentedTextWriter(writer, "    ");
25                    
26                     codeProvider.GenerateCodeFromCompileUnit(razorResults.GeneratedCode, indentwriter, options);
27                     indentwriter.Flush();
28                     indentwriter.Close();
29                     LastGeneratedCode = writer.GetStringBuilder().ToString();
30                     
31 
32                 }
33                 
34             }
35             return razorResults;
36            
37         }

代碼中的17行-32行是根據CodeDom 來生成C#代碼的,如果你只是要生成Assembly,不需要看到生成的代碼的話,那么17-32  行不是必須的。代碼中的第4行到第9行,使在准備一個 RazorEngineHost 實例。

 var host = new RazorEngineHost(new CSharpRazorCodeLanguage()); 是生成一個RazorEngineHost 的實例,new CSharpRazorCodeLanguage() 參數指定模板所用的語言是C#。

host.DefaultBaseClass = typeof(TemplateBase).FullName; 是指定生成的模板類的基類 這里是 前面代碼定義的模板基類。

host.DefaultNamespace =defaultnamespace;
host.DefaultClassName = defaultclassname; 這兩句代碼是指定了生成的模板類的命名空間和類名。

host.NamespaceImports.Add("System"); 是 導入命名空間,模板引擎將根據 這里導入的命名空間,產生C# using 代碼,這一句將會產生 using System;

 host.GeneratedClassContext = new GeneratedClassContext("Execute","Write", "WriteLiteral"); 這一句代碼的含義是 產生的模板類的 方法對應關系。要跟上面定義的模板基類的方法名稱對應。

代碼中的第12 -15句代碼 就是調用模板引擎 產生CodeDom。

第三、模板的執行

 1   private string ExecuteInternal(GeneratorResults razorResults, string defaultnamespace, string defaultclassname, dynamic Model)
 2         {
 3             
 4             using (var provider = new CSharpCodeProvider())
 5             {
 6                 var compiler = new CompilerParameters(); 
 7                 compiler.ReferencedAssemblies.Add("System.dll");
 8                 compiler.ReferencedAssemblies.Add("System.Core.dll");
 9                 compiler.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
10                 compiler.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
11                 compiler.GenerateInMemory = true;
12                 var result = provider.CompileAssemblyFromDom(compiler, razorResults.GeneratedCode);
13                 if (result.Errors.HasErrors) 
14                 {
15                     var error = result.Errors.OfType<CompilerError>().Where(i => !i.IsWarning).FirstOrDefault();
16                     if (error != null) throw new Exception(error.ErrorText); //拋出錯誤
17                 }
18                 TemplateBase temp= (TemplateBase)result.CompiledAssembly.CreateInstance(defaultnamespace+"."+defaultclassname);
19                 temp.Model = Model;
20                 try
21                 {
22                     temp.Execute();
23                 }
24                 catch (Exception ex)
25                 {
26                     throw new Exception("執行錯誤",ex);
27                 }
28                 return temp.Output.ToString();
29             }
30             
31 
32         }

 

 這段代碼就是調用第二步產生的CodeDom,編譯生成 Assembly,並執行模板。這里需要解釋一下的是。

compiler.ReferencedAssemblies.Add("System.dll");
compiler.ReferencedAssemblies.Add("System.Core.dll");
compiler.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
compiler.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);

這4句,就是引入編譯所需的Assembly。 其中System.Core.dll 和 Microsoft.CSharp.dll 是讓模板具有dynamic 對象的功能。最后一句是引入當前的程序集。

第四、模板引擎最后的調用是這樣調用的。

1  Engine engine = new Engine();
2             dynamic model= new AnonymousDynamicType(new {Text=txtInput.Text});
3             string result = engine.Execute(txtCode.Text, "_RazorDemo", GetSafeClassName(),model );
4             txtExecuteResult.Text = result;
5             txtCodeResult.Text = engine.LastGeneratedCode;

 

 其中第2行 AnonymousDynamicType 是把匿名類包裝成一個動態類型。

附上本文的代碼:

 http://files.cnblogs.com/NCodeGenerate/RazorDemo.zip

 

附: NCodeGenerate 新增了  NCodeGenerate.DBSchema 的文檔,在下面的文檔連接中。

附上下載地址:

NCodeGenerate.zip

文檔:文檔

 

NCodeGenerate 系列文章:

一、代碼生成利器-NCodeGenerate 是什么?

二、代碼生成利器-NCodeGenerate 教程(1) 遍歷數據庫內的所有表

三、代碼生成利器-NCodeGenerate 教程(2) NCodeGenerate的代碼公用之一

四、代碼生成利器-NCodeGenerate 教程(3) 生成代碼到文件.

五、代碼生成利器-NCodeGenerate 教程(4) CodeSmith模板轉換

六、代碼生成利器-NCodeGenerate 教程(5) 多種數據庫的支持

七、代碼生成利器-NCodeGenerate 教程(6) 調試功能 NTrace 輸出

八、代碼生成利器-NCodeGenerate 教程(7) 揭開調試功能 的神秘面紗

九、代碼生成利器-NCodeGenerate 教程(8) 揭開Razor模板引擎的神秘面紗

10、代碼生成利器-NCodeGenerate 教程(9) 數據類型映射Map功能

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM