Visual Studio下的代码生成T4模板使用说明.docx
- 文档编号:6015796
- 上传时间:2023-01-03
- 格式:DOCX
- 页数:18
- 大小:119.10KB
Visual Studio下的代码生成T4模板使用说明.docx
《Visual Studio下的代码生成T4模板使用说明.docx》由会员分享,可在线阅读,更多相关《Visual Studio下的代码生成T4模板使用说明.docx(18页珍藏版)》请在冰豆网上搜索。
VisualStudio下的代码生成T4模板使用说明
VisualStudio下的代码生成:
T4模板使用说明
在《基于T4的代码生成方式》中,我对T4模板的组成结构、语法,以及T4引擎的工作原理进行了大体的介绍,并且编写了一个T4模板实现了如何将一个XML转变成C#代码。
为了让由此需求的读者对T4有更深的了解,我们通过T4来做一些更加实际的事情——SQLGenerator。
在这里,我们可以通过SQLGenerator为某个数据表自动生成进行插入、修改和删除的存储过程。
[文中源代码从这里下载]
一、代码生成器的最终使用效果
我们首先来看看通过直接适用我们基于T4的SQL生成模板达到的效果。
右图(点击看大图)是VS2010的SolutionExplorer,在Script目录下面,我定义了三个后缀名为.tt的T4模板。
它们实际上是基于同一个数据表(T_PRODUCT)的三个存储过程的生成创建的模板文件,其中P_PRODUCT_D.tt、P_PRODUCT_I.tt和P_PRODUCT_D.tt分别用于记录的删除、插入和修改。
自动生成的扩展名为.sql的同名附属文件就是相应的存储过程。
基于三种不同的数据操作(Insert、Update和Delete),我创建了3个重用的、与具体数据表无关的模板:
InsertProcedureTemplate、UpdateProcedureTemplate和DeleteProcedureTemplate。
这样做的目的为为了实现最大的重用,如果我们需要为某个数据表创建相应的存储过程的时候,我们可以直接使用它们传入相应的数据表名就可以了。
实际上,P_PRODUCT_D.tt、P_PRODUCT_I.tt和P_PRODUCT_D.tt这三个T4模板的结构很简单,它们通过<#@include>指令将定义着相应ProcedureTemplate的T4模板文件包含进来。
最终的存储过程脚本通过调用ProcudureTempalte的Render方法生成。
其中构造函数的参数表示的分别是连接字符串名称(在配置文件中定义)和数据表的名称。
<#@templatelanguage="C#"hostspecific="True"#>
<#@outputextension="sql"#>
<#@includefile="T4Toolbox.tt"#>
<#@includefile="..\Templates\DeleteProcedureTemplate.tt"#>
<#
newDeleteProcedureTemplate("TestDb","T_PRODUCT").Render();
#>
<#@templatelanguage="C#"hostspecific="True"#>
<#@outputextension="sql"#>
<#@includefile="T4Toolbox.tt"#>
<#@includefile="..\Templates\InsertProcedureTemplate.tt"#>
<#
newInsertProcedureTemplate("TestDb","T_PRODUCT").Render();
#>
<#@templatelanguage="C#"hostspecific="True"#>
<#@outputextension="sql"#>
<#@includefile="T4Toolbox.tt"#>
<#@includefile="..\Templates\UpdateProcedureTemplate.tt"#>
<#
newUpdateProcedureTemplate("TestDb","T_PRODUCT").Render();
#>
二、安装T4工具箱(ToolBox)和编辑器
VS本身只提供一套基于T4引擎的代码生成的执行环境,为了利于你的编程你可以安装一些辅助性的东西。
T4ToolBox是一个CodePlex上开源的工具,它包含一些可以直接使用的代码生成器,比如EnumSQLView、AzManwrapper、LINQtoSQLclasses、LINQtoSQLschema和EntityFrameworkDAL等。
T4ToolBox还提供一些基于T4方面的VS的扩展。
当你按照之后,在“AddNewItem”对话框中就会多出一个命名为“CodeGeneration”的类别,其中包括若干文件模板。
下面提供的T4模板的编辑工作依赖于这个工具。
为了提高编程体验,比如智能感知以及代码配色,我们还可以安装一些第三方的T4编辑器。
我使用的是一个叫做OlegSych的T4Editor。
它具有免费版本和需要付费的专业版本,当然我使用的免费的那款。
成功按装了,它也会在AddNewItem”对话框中提供相应的基于T4的文件模板。
三、创建数据表
T4模板就是输入和输出的一个适配器,这与XSLT的作用比较类似。
对于我们将要实现的SQLGenerator来说,输入的是数据表的结构(Schema)输出的是最终生成的存储过程的SQL脚本。
对于数据表的定义,不同的项目具有不同标准。
我采用的是我们自己的数据库标准定义的数据表:
T_PRODUCT(表示产品信息),下面是创建表的脚本。
CREATETABLE[dbo].[T_PRODUCT](
[ID][VARCHAR](50)NOTNULL,
[NAME][NVARCHAR]NOTNULL,
[PRICE][float]NOTNULL,
[TOTAL_PRICE][FLOAT]NOTNULL,
[DESC][NVARCHAR]NULL,
[CREATED_BY][VARCHAR](50)NULL,
[CREATED_ON][DATETIME]NULL,
[LAST_UPDATED_BY][VARCHAR](50)NULL,
[LAST_UPDATED_ON][DATETIME]NULL,
[VERSION_NO][TIMESTAMP]NULL,
[TRANSACTION_ID][VARCHAR](50)NULL,
CONSTRAINT[PK_T_PRODUCT]PRIMARYKEYCLUSTERED([ID]ASC)ON[PRIMARY])
每一个表中有6个公共的字段:
CREATED_BY、CREATED_ON、LAST_UPDATED_BY、LAST_UPDATED_ON、VERSION_NO和TRANSACTION_ID分别表示记录的创建者、创建时间、最新更新者、最新更新时间、版本号(并发控制)和事务ID。
四、创建抽象的模板:
ProcedureTemplate
我们需要为三不同的数据操作得存储过程定义不同的模板,但是对于这三种存储过程的SQL结构都是一样的,基本结果可以通过下面的SQL脚本表示。
IFOBJECT_ID('<
DROPPROCEDURE<
GO
CREATEPROCEDURE<
(
<
)
AS
<
GO
为此我定义了一个抽象的模板:
ProcedureTemplate。
为了表示CUD三种不同的操作,我通过T4模板的“类特性块”(ClassFeatureBlock)定义了如下一个OperationKind的枚举。
<#+
publicenumOperationKind
{
Insert,
Update,
Delete
}
#>
然后下面就是整个ProcedureTemplate的定义了。
ProcedureTemplate直接继承自T4Toolbox.Template(来源于T4ToolBox,它继承自TextTransformation)。
ProcedureTemplate通过SMO(SQLServerManagementObject)获取数据表的结构(Schema)信息,所以我们需要应用SMO相关的程序集和导入相关命名空间。
ProcedureTemplate具有三个属性DatabaseName(表示连接字符串名称)、Table(SMO中表示数据表)和OperationKind(表示具体的CUD操作的一种),它们均通过构造函数初始化。
<#@assemblyname="Microsoft.SqlServer.ConnectionInfo"#>
<#@assemblyname="Microsoft.SqlServer.Smo"#>
<#@assemblyname="Microsoft.SqlServer.Management.Sdk.Sfc"#>
<#@importnamespace="System"#>
<#@importnamespace="Microsoft.SqlServer.Management.Smo"#>
<#+
publicabstractclassProcedureTemplate:
Template
{
publicstringDatabaseName{get;privateset;}
publicOperationKindOperationKind{get;privateset;}
publicTableTable{get;privateset;}
publicconststringVersionNoField="VERSION_NO";
publicconststringVersionNoParameterName="@p_version_no";
publicProcedureTemplate(stringdatabaseName,stringtableName,OperationKindoperationKind)
{
Guard.ArgumentNotNullOrEmpty(databaseName,"databaseName");
Guard.ArgumentNotNullOrEmpty(tableName,"tableName");
this.DatabaseName=databaseName;
this.OperationKind=operationKind;
Serverserver=newServer();
Databasedatabase=newDatabase(server,DatabaseName);
this.Table=newTable(database,tableName);
this.Table.Refresh();
}
publicvirtualstringGetProcedureName()
{
switch(this.OperationKind)
{
caseOperationKind.Insert:
return"P_"+this.Table.Name.Remove(0,2)+"_I";
caseOperationKind.Update:
return"P_"+this.Table.Name.Remove(0,2)+"_U";
default:
return"P_"+this.Table.Name.Remove(0,2)+"_D";
}
}
protectedvirtualstringGetParameterName(stringcolumnName)
{
return"@p_"+columnName.ToLower();
}
protectedabstractvoidRenderParameterList();
protectedabstractvoidRenderProcedureBody();
publicoverridestringTransformText()
{
Serverserver=newServer();
Databasedatabase=newDatabase(server,DatabaseName);
Tabletable=newTable(database,this.Table.Name);
table.Refresh();
#>
IFOBJECT_ID('[dbo].[<#=GetProcedureName()#>]','P')ISNOTNULL
DROPPROCEDURE[dbo].[<#=GetProcedureName()#>]
GO
CREATEPROCEDURE[dbo].[<#=GetProcedureName()#>]
(
<#+
PushIndent("\t");
this.RenderParameterList();
PopIndent();
#>
)
AS
<#+
PushIndent("\t");
this.RenderProcedureBody();
PopIndent();
PopIndent();
WriteLine("\nGO");
returnthis.GenerationEnvironment.ToString();
}
}
#>
存储过程的参数我们采用小写形式,直接在列名前加上一个"p_”(Parameter)前缀,列名到参数名之间的转化通过方法GetParameterName实现。
存储过程名称通过表明转化,转化规则为:
将"T_”(Table)改成"P_”(Procedure)前缀,并添加"_I"、"_U"和"_D"表示相应的操作类型,存储过程名称的解析通过GetProcedureName实现。
整个存储过程的输出通过方法TransformText输出,并通过PushIndent和PopIndent方法控制缩进。
由于CUD存储只有两个地方不一致:
参数列表和存储过程的主体,我定义了两个抽象方法RenderParameterList和RenderProcedureBody让具体的ProcedureTemplate去实现。
五、为CUD操作创建具体模板
基类ProcedureTemplate已经定义出了主要的转化规则,我们现在需要做的就是通过T4模板创建3个具体的ProcedureTemplate,分别实现针对CUD存储过程的生成。
为此我创建了三个继承自ProcedureTemplate的具体类:
InsertProcedureTemplate、UpdateProcedureTemplate和DeleteProcedureTemplate,它只需要实现RenderParameterList和RenderProcedureBody这两个抽象方法既即可,下面是它们的定义。
<#@includefile="ProcedureTemplate.tt"#>
<#+
publicclassInsertProcedureTemplate:
ProcedureTemplate
{
publicInsertProcedureTemplate(stringdatabaseName,stringtableName):
base(databaseName,tableName,OperationKind.Insert){}
protectedoverridevoidRenderParameterList()
{
for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(column.Name! =VersionNoField) { if(i { WriteLine("{0,-20}[{1}],",GetParameterName(column.Name),column.DataType.Name.ToUpper()); } else { WriteLine("{0,-20}[{1}]",GetParameterName(column.Name),column.DataType.Name.ToUpper()); } } } } protectedoverridevoidRenderProcedureBody() { WriteLine("INSERTINTO[dbo].[{0}]",this.Table.Name); WriteLine("("); PushIndent("\t"); for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(column.Name! =VersionNoField) { if(i { WriteLine("["+column.Name+"],"); } else { WriteLine("["+column.Name+"]"); } } } PopIndent(); WriteLine(")"); WriteLine("VALUES"); WriteLine("("); PushIndent("\t"); for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(column.Name! =VersionNoField) { if(i { WriteLine(GetParameterName(column.Name)+","); } else { WriteLine(GetParameterName(column.Name)); } } } PopIndent(); WriteLine(")"); } } #> <#@includefile="ProcedureTemplate.tt"#> <#+ publicclassUpdateProcedureTemplate: ProcedureTemplate { publicUpdateProcedureTemplate(stringdatabaseName,stringtableName): base(databaseName,tableName,OperationKind.Update) {} protectedoverridevoidRenderParameterList() { for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(i { WriteLine("{0,-20}[{1}],",GetParameterName(column.Name),column.DataType.Name.ToUpper()); } else { WriteLine("{0,-20}[{1}]",GetParameterName(column.Name),column.DataType.Name.ToUpper()); } } } protectedoverridevoidRenderProcedureBody() { WriteLine("UPDATE[dbo].[{0}]",this.Table.Name); WriteLine("SET"); PushIndent("\t"); for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(! column.InPrimaryKey) { if(i { WriteLine("{0,-20}={1},","["+column.Name+"]",this.GetParameterName(column.Name)); } else { WriteLine("{0,-20}={1}","["+column.Name+"]",this.GetParameterName(column.Name)); } } } PopIndent(); WriteLine("WHERE"); PushIndent("\t"); for(inti=0;i { Columncolumn=this.Table.Columns[i]; if(column.InPrimaryKey) { WriteLine("{0,-20}={1}AND","["+column.Name+"]",GetParameterName(column.Name)); } } WriteLine("{0,-20}={1}","["+VersionNoField+"]",VersionNoParameterName); PopIndent(); } } #> <#@includefile="ProcedureTemplate.tt"#> <#+ publicclassDeleteProcedureTemplate: ProcedureTemplate { publicDeleteProcedureTemplate(stringdatabaseName,stringtableName): base(databaseName,tableName,OperationKind.Delete){} protectedoverridevoidRenderParameterList() { foreach(Columncolumn
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Visual Studio下的代码生成T4模板使用说明 Studio 代码 生成 T4 模板 使用说明