模版引擎XTemplate是一个仿T4设计的引擎,功能上基本与T4一致(模版语法上完全兼容T4,模版头指令部分兼容)。
自己设计模版引擎,就是为了代码生成器、网站模版、邮件模版等多种场合,也就是要能拿出来单独使用、功能强大并且容易控制的。T4是个很好的引擎,但是它的设计基本上倾向于vs,几乎不顾别的场合。
XTemplate特点如下:
1&#xff0c;完全使用C#作为模版语言。跟ASP、ASP.Net页面的解析一样&#xff0c;把<##>标签外的文本内容当作字符串&#xff0c;用一个StringBuilder&#xff0c;标签内作为C#原生代码&#xff0c;拼在一起编译&#xff0c;进行模版替换时&#xff0c;实质上就是执行编译后的程序集&#xff0c;这就是XTemplate的核心原理&#xff01;网络上现有的许许多多模版引擎&#xff0c;要么采用标签替换&#xff0c;要么自创模版语言&#xff0c;这些都增加了使用者的学习难度。XTemplate使用C#作为模版语言&#xff0c;这个世界安静了&#xff01;
2&#xff0c;支持“调试”。不是运行时调试&#xff0c;而是XTemplate能够把模版编译的中间类文件以及程序集等输出&#xff0c;方便检查错误。如果把模版编译后的程序集保存下来&#xff0c;可以在没有模版文件的情况下直接使用模版功能。
3&#xff0c;不需要ASP.Net支持。有部分模版引擎&#xff0c;是模拟一个ASP.Net服务器&#xff0c;然后以ASP.Net作为模版来实现&#xff0c;这就要求有一个ASP.Net服务器作为宿主&#xff0c;限制了模版引擎的使用范围。
4&#xff0c;支持批量编译。可以把多个模版放入模版处理器&#xff0c;进行一次编译&#xff08;所有模版类都编译到一个程序集里面去&#xff09;。
5&#xff0c;支持类成员。模版内容默认情况下将会统一编译到一个类的Render方法里面去&#xff0c;但是有时候我们需要给这个类增加一些属性和方法&#xff0c;此时可以使用<#! #>标签&#xff0c;序数为单数表示开始&#xff0c;序数为偶数表示结束&#xff0c;所以不限制类成员代码的位置&#xff08;T4要求只能写在模版的最后面&#xff09;。
6&#xff0c;支持自定义基类。默认情况下&#xff0c;所有编译生成的模版类都继承自TemplateBase&#xff0c;你也可以创建自己的模版基类&#xff0c;然后在模版头通过指令&#xff0c;或者通过外部宿主指定自定义的模版基类&#xff0c;模版中可以直接使用自定义模版基类的成员&#xff08;因为继承嘛&#xff09;&#xff0c;比如代码生成器XCoder中的XCoderBase。
7&#xff0c;自动引用宿主程序集。T4在使用上最大的麻烦就是引用外部程序集和命名空间&#xff0c;毕竟不是在vs里面编写C#代码。XTemplate在编译的时候&#xff0c;自动引用宿主&#xff08;就是调用者&#xff0c;比如XCoder&#xff09;的所有应用程序集&#xff0c;同时引用大部分常用的明明空间&#xff0c;因为这样&#xff0c;生成的类很臃肿&#xff0c;但是编译的时候&#xff0c;编译器会自动去掉无用的引用。XTemplate从完成到现在为止&#xff0c;还没有用过引用程序集和命名空间的问题&#xff0c;因为一般来说&#xff0c;模版中需要用到的程序集&#xff0c;宿主里面一般都有用到&#xff0c;非常符合我们的使用习惯。
8&#xff0c;与宿主的良好交互。在XTemplate中&#xff0c;编译的模版程序集是直接加载在默认域&#xff0c;这点与T4不同&#xff0c;T4会新建一个域&#xff0c;应该是为了防止模版代码弄脏默认域的数据吧&#xff08;比如干扰vs运行&#xff09;。因为在同一个域&#xff0c;XTemplate与宿主进行交互&#xff0c;就不需要“FQ”&#xff08;跨域&#xff09;了。XTemplate的处理过程分为分析、编译和执行三步&#xff0c;都可以由外部控制&#xff0c;比如有时候我们只是需要检查一下模版的语法&#xff0c;只需要检查一下模版语法是否正确&#xff0c;这个时候编译一下就可以了。
9&#xff0c;更多的特点需要大家来发现&#xff01;
XCoder使用XTemplate代码&#xff08;后面有XCoder的项目代码&#xff09;&#xff1a;
Dictionary<String, Object> data &#61; new Dictionary<string, object>();
data["Config"] &#61; Config;
data["Tables"] &#61; Tables;
data["Table"] &#61; table;// 声明模版引擎
Template tt &#61; new Template();
Template.Debug &#61; Config.Debug;
foreach (String item in ss)
{if (item.EndsWith("scc", StringComparison.Ordinal)) continue;String tempFile &#61; item;if (!Path.IsPathRooted(tempFile) && !tempFile.StartsWith(TemplatePath, StringComparison.OrdinalIgnoreCase))tempFile &#61; Path.Combine(TemplatePath, tempFile);String content &#61; File.ReadAllText(tempFile);// 添加文件头if (Config.UseHeadTemplate && !String.IsNullOrEmpty(Config.HeadTemplate))content &#61; Config.HeadTemplate &#43; content;tt.AddTemplateItem(item, content);
}
tt.Process();// 编译模版
tt.Compile();List<String> rs &#61; new List<string>();
foreach (String item in ss)
{if (item.EndsWith("scc", StringComparison.Ordinal)) continue;//String content &#61; RenderFile(table, item, data);String content &#61; tt.Render(item, data);// 计算输出文件名String fileName &#61; Path.GetFileName(item);String className &#61; CutPrefix(table.Name);className &#61; FixWord(className);String remark &#61; table.Description;if (String.IsNullOrEmpty(remark)) remark &#61; ENameToCName(className);if (Config.UseCNFileName && !String.IsNullOrEmpty(remark)) className &#61; remark;fileName &#61; fileName.Replace("类名", className).Replace("类说明", remark).Replace("连接名", Config.EntityConnName);fileName &#61; Path.Combine(OuputPath, fileName);File.WriteAllText(fileName, content, Encoding.UTF8);rs.Add(content);
}
XTemplate设计图&#xff08;我喜欢先做图再编码&#xff09;&#xff1a;