现在的位置: 首页 > 综合 > 正文

代码生成利器-NCodeGenerate 教程(8) 揭开Razor模板引擎的神秘面纱代码生成利器-NCodeGenerate 是什么?代码生成利器-NCodeGenerate 教程(1) 遍历数据库内的所有表代码生成利器-NCodeGenerate 教程(2) NCodeGenerate的代码公用之一代码生成利器-NCodeGenerate 教程(3) 生成代码到文件.代码生成利器-NCodeGenerate 教程(4) CodeSmith模板转换代码生成利器-NCodeGenerate 教程

2012年12月25日 ⁄ 综合 ⁄ 共 5940字 ⁄ 字号 评论关闭

  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功能

 

 

 

 

抱歉!评论已关闭.