我在去年的一篇关于T4:Text Template Transformation Toolkit提到的早已发布的微软官方代码生成引擎T4, 一直以来在社区状态不温不火, 想来有以下原因:
- 参考资源少, 微软以前就没正经推广过它.
- 没有好的编辑器, 不是谁都对T4语法, 函数了然于胸的, 语法高亮, 智能提示一个没有, 直接就是一个入门的极高门槛.
- 只能单文件输出, T4的输出文件, 只能依附在T4模板下, 不能指定输出路径, 生成了文件还要拷来拷去, 效率很低.
由以上的几个主要因素, 决定了长期以来, 要么使用通用的代码生成商业工具如CodeSmith, 要么使用自己写的专用工具, T4的位置在社区里很尴尬.
不过, 现在T4的生存状况已经好了许多.
虽然官方的参考资源还是很少, 但CodePlex上已经出现了不少提供T4模板的项目, 商业工具Visual T4 Editor的出现, 使得T4在实际开发中投入应用成为可能. Visual T4 Editor有免费的社区版和收费的专业版两个版本, 社区版只有简单的语法高亮, 而专业版已经提供了智能提示, 我同事当时还赶紧买了一个专业版的授权. 紧接着语法高亮在MonoDevelop里也被实现了,而终于能提供智能提示的免费的社区版tangible T4 Editor出现,则直接将Visual T4 Editor轰成废柴, 我现在用着tangible T4 Editor,对Visual T4 Editor一点念想也不留了。
阻挡T4进入主流的最后一个障碍,不能指定输出而只能生成模板的附属文件的局限,也被Damien Guard老兄最近解决了,一看到他的这篇multiple outputs from t4 made easy,我欣喜若狂,立即就将T4投入了我手头的这个项目中。
我的这套T4模板使用如下图。
没有一切花哨,结合了Damien Guard的多文件路径输出思路,CodeSmith的数据库模型,.netTiers的三层架构框子,以及对Visual Studio IDE扩展性的应用,我写了以上T4模板:
- SqlSchema.ttinclude, 用于为代码生成提供仅基于SQL Server数据库引擎的数据模型。
- RenderContext.ttinclude, 用于实现对Visual Studio的EnvDT模型的访问以及多文件路径输出。
- RenderHelper.ttinclude, 提供一些在代码生成过程中需要的可重用方法。
- Entry.tt, 代码生成入口。
- 其他的ttinclude文件就都是业务代码的模板实现了。
这些模板的使用很简单,将CodeGenerator这个项目包含在实际应用的解决方案中。在Entry模板中指定要生成的数据库表名,三层结构中每一层对应的项目名称,保存,T4就自动将生成的代码文件输出到指定文件路径且包含在项目中了,如果项目处于源码控制状态,还能自动Check Out。
Entry的代码很简单:
<#@ template language="C#v3.5" hostSpecific="true" inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #><#@ include file="RenderHelper.ttinclude" #><#@ include file="DataEntity.ttinclude" #><#@ include file="DataProvider.ttinclude" #><#@ include file="BusinessService.ttinclude" #><#@ include file="ListPageAspx.ttinclude" #><#@ include file="ListPageCs.ttinclude" #><#@ include file="EditPageAspx.ttinclude" #><#@ include file="EditPageCs.ttinclude" #><#@ output encoding="UTF8" extension=".txt" #><#@ import namespace="System" #><#@ import namespace="System.Collections" #><#+ public class Consts{ public const string ConnectionString = "server=.;database=prototype;Integrated Security=SSPI;"; public static readonly String[] TableNames = new String[]{"Roles"}; public const bool DataEntityGenerated = false; public const string DataEntityProjectName = "DataAccess"; public const string DataEntityGenerateFolder = "Entities"; public const bool DataProviderGenerated = false; public const string DataProviderProjectName = "DataAccess"; public const string DataProviderGenerateFolder = "Providers"; public const bool BusinessServiceGenerated = false; public const string BusinessServiceProjectName = "BusinessManager"; public const string BusinessServiceGenerateFolder = ""; public const bool ListPageGenerated = false; public const string ListPageProjectName = "PortalHost"; public const string ListPageGenerateFolder = "SystemManagement"; public const bool EditPageGenerated = false; public const string EditPageProjectName = "PortalHost"; public const string EditPageGenerateFolder = "SystemManagement"; } #>
实际代码生成模板的应用也很简单,以DataEntity.ttinclude为例:
<#@ template language="C#v3.5" hostSpecific="true" #><# if(Consts.TableNames != null && Consts.DataEntityGenerated) { if(Context == null) Context = RenderContext.Create(Host, GenerationEnvironment); if(Database == null) Database = new DatabaseSchema(Consts.ConnectionString); foreach(var SourceTableName in Consts.TableNames){ if(string.IsNullOrEmpty(SourceTableName) || !Database.Tables.Contains(SourceTableName)) continue; string className = GetEntityClassName(SourceTableName); TableSchema SourceTable = Database.Tables[SourceTableName]; ColumnSchemaCollection cols = SourceTable.Columns; Context.BeginRender(className + ".generated.cs", Consts.DataEntityProjectName, Consts.DataEntityGenerateFolder); #>实际业务代码生成,包括表名单复数转换,下划线转Pascal命名,读取数据库描述...
<# Context.EndRender(); } Context.Process(); } #>
我看看这次T4在项目中的应用反响如何,下个月把这套模板放出来,请诸君提提意见。
浙公网安备 33010602011771号