创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇]
在《基于T4的代码生成方式》中,我对T4模板的组成结构、语法,以及T4引擎的工作原理进行了大体的介绍,并且编写了一个T4模板实现了如何将一个XML转变成C#代码。为了让由此需求的读者对T4有更深的了解,我们通过T4来做一些更加实际的事情——SQL Generator。在这里,我们可以通过SQL Generator为某个数据表自动生成进行插入、修改和删除的存储过程。[文中源代码从这里下载]
一、代码生成器的最终使用效果
我们首先来看看通过直接适用我们基于T4的SQL生成模板达到的效果。右图(点击看大图)是VS2010的Solution Explorer,在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方法生成。其中构造函数的参数表示的分别是连接字符串名称(在配置文件中定义)和数据表的名称。
<#@ template language="C#" hostspecific="True" #>
<!--CRLF-->
<#@ output extension="sql" #>
<!--CRLF-->
<#@ include file="T4Toolbox.tt" #>
<!--CRLF-->
<#@ include file="..\Templates\DeleteProcedureTemplate.tt" #>
<!--CRLF-->
<#
<!--CRLF-->
new DeleteProcedureTemplate("TestDb","T_PRODUCT").Render();
<!--CRLF-->
#>
<!--CRLF-->
<#@ template language="C#" hostspecific="True" #>
<!--CRLF-->
<#@ output extension="sql" #>
<!--CRLF-->
<#@ include file="T4Toolbox.tt" #>
<!--CRLF-->
<#@ include file="..\Templates\InsertProcedureTemplate.tt" #>
<!--CRLF-->
<#
<!--CRLF-->
new InsertProcedureTemplate("TestDb","T_PRODUCT").Render();
<!--CRLF-->
#>
<!--CRLF-->
<#@ template language="C#" hostspecific="True" #>
<!--CRLF-->
<#@ output extension="sql" #>
<!--CRLF-->
<#@ include file="T4Toolbox.tt" #>
<!--CRLF-->
<#@ include file="..\Templates\UpdateProcedureTemplate.tt" #>
<!--CRLF-->
<#
<!--CRLF-->
new UpdateProcedureTemplate("TestDb","T_PRODUCT").Render();
<!--CRLF-->
#>
<!--CRLF-->
二、安装T4工具箱(ToolBox)和编辑器
VS本身只提供一套基于T4引擎的代码生成的执行环境,为了利于你的编程你可以安装一些辅助性的东西。T4 ToolBox是一个CodePlex上开源的工具,它包含一些可以直接使用的代码生成器,比如Enum SQL View、AzMan wrapper、LINQ to SQL classes、LINQ to SQL schema和Entity Framework DAL等。T4 ToolBox还提供一些基于T4方面的VS的扩展。当你按照之后,在“Add New Item”对话框中就会多出一个命名为“Code Generation”的类别,其中包括若干文件模板。下面提供的T4模板的编辑工作依赖于这个工具。
为了提高编程体验,比如智能感知以及代码配色,我们还可以安装一些第三方的T4编辑器。我使用的是一个叫做Oleg Sych的T4 Editor。它具有免费版本和需要付费的专业版本,当然我使用的免费的那款。成功按装了,它也会在Add New Item”对话框中提供相应的基于T4 的文件模板。
三、创建数据表
T4模板就是输入和输出的一个适配器,这与XSLT的作用比较类似。对于我们将要实现的SQL Generator来说,输入的是数据表的结构(Schema)输出的是最终生成的存储过程的SQL脚本。对于数据表的定义,不同的项目具有不同标准。我采用的是我们自己的数据库标准定义的数据表:T_PRODUCT(表示产品信息),下面是创建表的脚本。
CREATE TABLE [dbo].[T_PRODUCT](
<!--CRLF-->
[ID] [VARCHAR](50) NOT NULL,
<!--CRLF-->
[NAME] [NVARCHAR] NOT NULL,
<!--CRLF-->
[PRICE] [float] NOT NULL,
<!--CRLF-->
[TOTAL_PRICE] [FLOAT] NOT NULL,
<!--CRLF-->
[DESC] [NVARCHAR] NULL,
<!--CRLF-->
<!--CRLF-->
[CREATED_BY] [VARCHAR](50) NULL,
<!--CRLF-->
[CREATED_ON] [DATETIME] NULL,
<!--CRLF-->
[LAST_UPDATED_BY] [VARCHAR](50) NULL,
<!--CRLF-->
[LAST_UPDATED_ON] [DATETIME] NULL,
<!--CRLF-->
[VERSION_NO] [TIMESTAMP] NULL,
<!--CRLF-->
[TRANSACTION_ID] [VARCHAR](50) NULL,
<!--CRLF-->
CONSTRAINT [PK_T_PRODUCT] PRIMARY KEY CLUSTERED( [ID] ASC)ON [PRIMARY])
<!--CRLF-->
每一个表中有6个公共的字段:CREATED_BY、CREATED_ON、LAST_UPDATED_BY、LAST_UPDATED_ON、VERSION_NO和TRANSACTION_ID分别表示记录的创建者、创建时间、最新更新者、最新更新时间、版本号(并发控制)和事务ID。
四、创建抽象的模板:ProcedureTemplate
我们需要为三不同的数据操作得存储过程定义不同的模板,但是对于这三种存储过程的SQL结构都是一样的,基本结果可以通过下面的SQL脚本表示。
IF OBJECT_ID( '<<ProcedureName>>', 'P' ) IS NOT NULL
<!--CRLF-->
DROP PROCEDURE <<ProcedureName>>
<!--CRLF-->
GO
<!--CRLF-->
<!--CRLF-->
CREATE PROCEDURE <<ProcedureName>>
<!--CRLF-->
(
<!--CRLF-->
<<ParameterList>>
<!--CRLF-->
)
<!--CRLF-->
AS
<!--CRLF-->
<!--CRLF-->
<<ProcedureBody>>
<!--CRLF-->
<!--CRLF-->
GO
<!--CRLF-->
为此我定义了一个抽象的模板:ProcedureTemplate。为了表示CUD三种不同的操作,我通过T4模板的“类特性块”(Class Feature Block)定义了如下一个OperationKind的枚举。
<#+
<!--CRLF-->
public enum OperationKind
<!--CRLF-->
{
<!--CRLF-->
Insert,
<!--CRLF-->
Update,
<!--CRLF-->
Delete
<!--CRLF-->
}
<!--CRLF-->
#>
<!--CRLF-->
然后下面就是整个ProcedureTemplate的定义了。ProcedureTemplate直接继承自T4Toolbox.Template(来源于T4 ToolBox,它继承自TextTransformation)。ProcedureTemplate通过SMO(SQL Server Management Object)获取数据表的结构(Schema)信息,所以我们需要应用SMO相关的程序集和导入相关命名空间。ProcedureTemplate具有两个属性Table(SMO中表示数据表)和OperationKind(表示具体的CUD操作的一种),它们均通过构造函数初始化。简单起见,我们没有指定Server,而默认采用本机指定的数据库。
1: <#@ assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<!--CRLF-->
2: <#@ assembly name="Microsoft.SqlServer.Smo" #>
<!--CRLF-->
3: <#@ assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
<!--CRLF-->
4: <#@ import namespace="System" #>
<!--CRLF-->
5: <#@ import namespace="Microsoft.SqlServer.Management.Smo" #>
<!--CRLF-->
6: <#+
<!--CRLF-->
7: public abstract class ProcedureTemplate : Template
<!--CRLF-->
8: {
<!--CRLF-->
9: public OperationKind OperationKind {get; private set;}
<!--CRLF-->
10: public Table Table {get; private set;}
<!--CRLF-->
11:
<!--CRLF-->
12: public const string VersionNoField = "VERSION_NO";
<!--CRLF-->
13: public const string VersionNoParameterName = "@p_version_no";
<!--CRLF-->
14:
<!--CRLF-->
15: public ProcedureTemplate(string databaseName, string tableName,OperationKind operationKind)
<!--CRLF-->
16: {
<!--CRLF-->
17: this.OperationKind = operationKind;
<!--CRLF-->
18: Server server = new Server();
<!--CRLF-->
19: Database database = new Database(server,databaseName);
<!--CRLF-->
20: this.Table = new Table(database, tableName);
<!--CRLF-->
21: this.Table.Refresh();
<!--CRLF-->
22: }
<!--CRLF-->
23:
<!--CRLF-->
24: public virtual string GetProcedureName()
<!--CRLF-->
25: {
<!--CRLF-->
26: switch(this.OperationKind)
<!--CRLF-->
27: {
<!--CRLF-->
28: case OperationKind.Insert: return "P_" +this.Table.Name.Remove(0,2) + "_I";
<!--CRLF-->
29: case OperationKind.Update: return "P_" +this.Table.Name.Remove(0,2) + "_U";
<!--CRLF-->
30: default: return "P_" +this.Table.Name.Remove(0,2) + "_D";
<!--CRLF-->
31: }
<!--CRLF-->
32: }
<!--CRLF-->
33:
<!--CRLF-->
34: protected virtual string GetParameterName(string columnName)
<!--CRLF-->
35: {
<!--CRLF-->
36: return "@p_" + columnName.ToLower();
<!--CRLF-->
37: }
<!--CRLF-->
38:
<!--CRLF-->
39: protected abstract void RenderParameterList();
<!--CRLF-->
40:
<!--CRLF-->
41: protected abstract void RenderProcedureBody();
<!--CRLF-->
42:
<!--CRLF-->
43: public override string TransformText()
<!--CRLF-->
44: {
<!--CRLF-->
45: #>
<!--CRLF-->
46: IF OBJECT_ID( '[dbo].[<#= GetProcedureName()#>]', 'P' ) IS NOT NULL
<!--CRLF-->
47: DROP PROCEDURE [dbo].[<#= GetProcedureName()#>]
<!--CRLF-->
48: GO
<!--CRLF-->
49:
<!--CRLF-->
50: CREATE PROCEDURE [dbo].[<#= GetProcedureName() #>]
<!--CRLF-->
51: (
<!--CRLF-->
52: <#+
<!--CRLF-->
53: PushIndent("\t");
<!--CRLF-->
54: this.RenderParameterList();
<!--CRLF-->
55: PopIndent();
<!--CRLF-->
56: #>
<!--CRLF-->
57: )
<!--CRLF-->
58: AS
<!--CRLF-->
59:
<!--CRLF-->
60: <#+
<!--CRLF-->
61: PushIndent("\t");
<!--CRLF-->
62: this.RenderProcedureBody();
<!--CRLF-->
63: PopIndent();
<!--CRLF-->
64: PopIndent();
<!--CRLF-->
65: WriteLine("\nGO");
<!--CRLF-->
66: return this.GenerationEnvironment.ToString();
<!--CRLF-->
67: }
<!--CRLF-->
68: }
<!--CRLF-->
69: #>
<!--CRLF-->
存储过程的参数我们采用小写形式,直接在列名前加上一个"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这两个抽象方法既即可,下面是它们的定义。
<#@ include file="ProcedureTemplate.tt" #>
<!--CRLF-->
<#+
<!--CRLF-->
public class InsertProcedureTemplate : ProcedureTemplate
<!--CRLF-->
{
<!--CRLF-->
public InsertProcedureTemplate(string databaseName, string tableName): base(databaseName,tableName,OperationKind.Insert){}
<!--CRLF-->
<!--CRLF-->
protected override void RenderParameterList()
<!--CRLF-->
{
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(column.Name != VersionNoField)
<!--CRLF-->
{
<!--CRLF-->
if(i<this.Table.Columns.Count -1)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}[{1}],", GetParameterName(column.Name),column.DataType.Name.ToUpper());
<!--CRLF-->
}
<!--CRLF-->
else
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}[{1}]", GetParameterName(column.Name),column.DataType.Name.ToUpper());
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
<!--CRLF-->
protected override void RenderProcedureBody()
<!--CRLF-->
{
<!--CRLF-->
WriteLine("INSERT INTO [dbo].[{0}]", this.Table.Name);
<!--CRLF-->
WriteLine("(");
<!--CRLF-->
PushIndent("\t");
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(column.Name != VersionNoField)
<!--CRLF-->
{
<!--CRLF-->
if(i<this.Table.Columns.Count -1)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("[" +column.Name + "],");
<!--CRLF-->
}
<!--CRLF-->
else
<!--CRLF-->
{
<!--CRLF-->
WriteLine("[" +column.Name + "]");
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
PopIndent();
<!--CRLF-->
WriteLine(")");
<!--CRLF-->
WriteLine("VALUES");
<!--CRLF-->
WriteLine("(");
<!--CRLF-->
PushIndent("\t");
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(column.Name != VersionNoField)
<!--CRLF-->
{
<!--CRLF-->
if(i<this.Table.Columns.Count -1)
<!--CRLF-->
{
<!--CRLF-->
WriteLine(GetParameterName(column.Name) + ",");
<!--CRLF-->
}
<!--CRLF-->
else
<!--CRLF-->
{
<!--CRLF-->
WriteLine(GetParameterName(column.Name));
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
<!--CRLF-->
}
<!--CRLF-->
PopIndent();
<!--CRLF-->
WriteLine(")");
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
#>
<!--CRLF-->
<#@ include file="ProcedureTemplate.tt" #>
<!--CRLF-->
<#+
<!--CRLF-->
public class UpdateProcedureTemplate : ProcedureTemplate
<!--CRLF-->
{
<!--CRLF-->
public UpdateProcedureTemplate(string databaseName, string tableName): base(databaseName,tableName,OperationKind.Update)
<!--CRLF-->
{}
<!--CRLF-->
<!--CRLF-->
protected override void RenderParameterList()
<!--CRLF-->
{
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(i<this.Table.Columns.Count -1)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}[{1}],", GetParameterName(column.Name),column.DataType.Name.ToUpper());
<!--CRLF-->
}
<!--CRLF-->
else
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}[{1}]", GetParameterName(column.Name),column.DataType.Name.ToUpper());
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
<!--CRLF-->
protected override void RenderProcedureBody()
<!--CRLF-->
{
<!--CRLF-->
WriteLine("UPDATE [dbo].[{0}]", this.Table.Name);
<!--CRLF-->
WriteLine("SET");
<!--CRLF-->
PushIndent("\t");
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(!column.InPrimaryKey)
<!--CRLF-->
{
<!--CRLF-->
if(i<this.Table.Columns.Count -1)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0,-20}= {1},", "[" +column.Name + "]", this.GetParameterName(column.Name));
<!--CRLF-->
}
<!--CRLF-->
else
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0,-20}= {1}", "[" +column.Name+"]", this.GetParameterName(column.Name));
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
PopIndent();
<!--CRLF-->
WriteLine("WHERE");
<!--CRLF-->
PushIndent("\t");
<!--CRLF-->
for(int i=0; i<this.Table.Columns.Count;i++)
<!--CRLF-->
{
<!--CRLF-->
Column column = this.Table.Columns[i];
<!--CRLF-->
if(column.InPrimaryKey)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}= {1} AND", "[" +column.Name + "]", GetParameterName(column.Name));
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
WriteLine("{0, -20}= {1}", "[" + VersionNoField + "]", VersionNoParameterName);
<!--CRLF-->
PopIndent();
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
#>
<!--CRLF-->
<#@ include file="ProcedureTemplate.tt" #>
<!--CRLF-->
<#+
<!--CRLF-->
public class DeleteProcedureTemplate : ProcedureTemplate
<!--CRLF-->
{
<!--CRLF-->
public DeleteProcedureTemplate(string databaseName, string tableName): base(databaseName,tableName,OperationKind.Delete){}
<!--CRLF-->
<!--CRLF-->
protected override void RenderParameterList()
<!--CRLF-->
{
<!--CRLF-->
foreach (Column column in this.Table.Columns)
<!--CRLF-->
{
<!--CRLF-->
if (column.InPrimaryKey)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}[{1}],", GetParameterName(column.Name),column.DataType.Name.ToUpper());
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
WriteLine("{0, -20}[{1}]", VersionNoParameterName, "TIMESTAMP");
<!--CRLF-->
}
<!--CRLF-->
<!--CRLF-->
protected override void RenderProcedureBody()
<!--CRLF-->
{
<!--CRLF-->
WriteLine("DELETE FROM [dbo].[{0}]", this.Table.Name);
<!--CRLF-->
WriteLine("WHERE");
<!--CRLF-->
PushIndent("\t\t");
<!--CRLF-->
foreach (Column column in this.Table.Columns)
<!--CRLF-->
{
<!--CRLF-->
if (column.InPrimaryKey)
<!--CRLF-->
{
<!--CRLF-->
WriteLine("{0, -20}= {1} AND", column.Name, GetParameterName(column.Name));
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
WriteLine("{0, -20}= {1}", VersionNoField, VersionNoParameterName);
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
#>
<!--CRLF-->
至于三个具体的ProcedureTemplate如何生成参数列表和主体部分,在这里就不在多做说明了。这里唯一需要强调的是:脚本的输出是通过TextTransformation的静态WriteLine方法实现,它和Console的同名方法使用一致。针对我们之前定义的数据表T_PRODUCT的结果,通过在文章开头定义的三个TT模板,最终将会生成如下的三个存储过程。
IF OBJECT_ID( '[dbo].[P_PRODUCT_I]', 'P' ) IS NOT NULL
<!--CRLF-->
DROP PROCEDURE [dbo].[P_PRODUCT_I]
<!--CRLF-->
GO
<!--CRLF-->
<!--CRLF-->
CREATE PROCEDURE [dbo].[P_PRODUCT_I]
<!--CRLF-->
(
<!--CRLF-->
@p_id [VARCHAR],
<!--CRLF-->
@p_name [NVARCHAR],
<!--CRLF-->
@p_price [FLOAT],
<!--CRLF-->
@p_total_price [FLOAT],
<!--CRLF-->
@p_desc [NVARCHAR],
<!--CRLF-->
@p_created_by [VARCHAR],
<!--CRLF-->
@p_created_on [DATETIME],
<!--CRLF-->
@p_last_updated_by [VARCHAR],
<!--CRLF-->
@p_last_updated_on [DATETIME],
<!--CRLF-->
@p_transaction_id [VARCHAR]
<!--CRLF-->
)
<!--CRLF-->
AS
<!--CRLF-->
<!--CRLF-->
INSERT INTO [dbo].[T_PRODUCT]
<!--CRLF-->
(
<!--CRLF-->
[ID],
<!--CRLF-->
[NAME],
<!--CRLF-->
[PRICE],
<!--CRLF-->
[TOTAL_PRICE],
<!--CRLF-->
[DESC],
<!--CRLF-->
[CREATED_BY],
<!--CRLF-->
[CREATED_ON],
<!--CRLF-->
[LAST_UPDATED_BY],
<!--CRLF-->
[LAST_UPDATED_ON],
<!--CRLF-->
[TRANSACTION_ID]
<!--CRLF-->
)
<!--CRLF-->
VALUES
<!--CRLF-->
(
<!--CRLF-->
@p_id,
<!--CRLF-->
@p_name,
<!--CRLF-->
@p_price,
<!--CRLF-->
@p_total_price,
<!--CRLF-->
@p_desc,
<!--CRLF-->
@p_created_by,
<!--CRLF-->
@p_created_on,
<!--CRLF-->
@p_last_updated_by,
<!--CRLF-->
@p_last_updated_on,
<!--CRLF-->
@p_transaction_id
<!--CRLF-->
)
<!--CRLF-->
<!--CRLF-->
GO
<!--CRLF-->
IF OBJECT_ID( '[dbo].[P_PRODUCT_U]', 'P' ) IS NOT NULL
<!--CRLF-->
DROP PROCEDURE [dbo].[P_PRODUCT_U]
<!--CRLF-->
GO
<!--CRLF-->
<!--CRLF-->
CREATE PROCEDURE [dbo].[P_PRODUCT_U]
<!--CRLF-->
(
<!--CRLF-->
@p_id [VARCHAR],
<!--CRLF-->
@p_name [NVARCHAR],
<!--CRLF-->
@p_price [FLOAT],
<!--CRLF-->
@p_total_price [FLOAT],
<!--CRLF-->
@p_desc [NVARCHAR],
<!--CRLF-->
@p_created_by [VARCHAR],
<!--CRLF-->
@p_created_on [DATETIME],
<!--CRLF-->
@p_last_updated_by [VARCHAR],
<!--CRLF-->
@p_last_updated_on [DATETIME],
<!--CRLF-->
@p_version_no [TIMESTAMP],
<!--CRLF-->
@p_transaction_id [VARCHAR]
<!--CRLF-->
)
<!--CRLF-->
AS
<!--CRLF-->
<!--CRLF-->
UPDATE [dbo].[T_PRODUCT]
<!--CRLF-->
SET
<!--CRLF-->
[NAME] = @p_name,
<!--CRLF-->
[PRICE] = @p_price,
<!--CRLF-->
[TOTAL_PRICE] = @p_total_price,
<!--CRLF-->
[DESC] = @p_desc,
<!--CRLF-->
[CREATED_BY] = @p_created_by,
<!--CRLF-->
[CREATED_ON] = @p_created_on,
<!--CRLF-->
[LAST_UPDATED_BY] = @p_last_updated_by,
<!--CRLF-->
[LAST_UPDATED_ON] = @p_last_updated_on,
<!--CRLF-->
[VERSION_NO] = @p_version_no,
<!--CRLF-->
[TRANSACTION_ID] = @p_transaction_id
<!--CRLF-->
WHERE
<!--CRLF-->
[ID] = @p_id AND
<!--CRLF-->
[VERSION_NO] = @p_version_no
<!--CRLF-->
<!--CRLF-->
GO
<!--CRLF-->
IF OBJECT_ID( '[dbo].[P_PRODUCT_D]', 'P' ) IS NOT NULL
<!--CRLF-->
DROP PROCEDURE [dbo].[P_PRODUCT_D]
<!--CRLF-->
GO
<!--CRLF-->
<!--CRLF-->
CREATE PROCEDURE [dbo].[P_PRODUCT_D]
<!--CRLF-->
(
<!--CRLF-->
@p_id [VARCHAR],
<!--CRLF-->
@p_version_no [TIMESTAMP]
<!--CRLF-->
)
<!--CRLF-->
AS
<!--CRLF-->
<!--CRLF-->
DELETE FROM [dbo].[T_PRODUCT]
<!--CRLF-->
WHERE
<!--CRLF-->
ID = @p_id AND
<!--CRLF-->
VERSION_NO = @p_version_no
<!--CRLF-->
<!--CRLF-->
GO
<!--CRLF-->
六、局限性
上面这个例子虽然很好实现了基于数据表的存储过程的生成,但是使用起来仍然不方便——我们需要为每一个需要生成出来的存储过程定义T4模板。也就是说在这种代码生成下,模板文件和生成文件之间是1:1的关系。实际上我们希望的方式是:创建一个基于某个表的TT文件,让它生成3个CUD三个存储过程;或者在一个TT文件中设置一个数据表的列表,让基于这些表的所有存储过程一并生成;或者直接子指定数据库,让所有数据表的存储过程一并生成出来。到底如何实现基于多文件的代码生成,请听《下回》分解。
创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇]相关推荐
- 创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]
在<上篇>中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加.修改和删除操作的存储过程.但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板.如果 ...
- t4b代码生成_Ef+T4模板实现代码快速生成器
效果如图,demo(点击demo可下载案例) 项目结构如图 T4BLL添加BLL.tt文件: T4Model添加Model文件: T4DAL添加DAL.tt文件: T4DAL 添加ADO.NET En ...
- t4模板生成html,强大的代码生成器——T4模板
T4 Editor工具下载地址 tangible T4 Editor 2.5.0 plus modeling tools for VS 2019 https://marketplace.visuals ...
- [转]MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码
本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:Hello ...
- 使用 Source Generator 代替 T4 动态生成代码
使用 Source Generator 代替 T4 动态生成代码 Intro 在 Source Generator 出现之前有一些重复性的代码,我会使用 T4 去生成,这样就可以一定程度上避免复制粘贴 ...
- 在Visual Studio中使用T4 Templates 生成代码
在没有看过Hilton Giesenow(How Do I: Create and Use T4 Templates.)的视频之前,我还没意识到在Visual Studio 2008 中使用T4是何等 ...
- 使用Runnable接口创建线程,很简单
大家好,今天分享.使用Runnable接口创建线程 首先Java创建线程可以通过三种方法: 即: 1.继承Thread类创建线程类(重点) 2.通过Runnable接口创建线程类(重点) 3.通过Ca ...
- mysql+代码备份,一个很简单的MYSQL数据库备份脚本代码
假设有三个库 m_site,m_bbs,m_cms. #!/bin/sh # # MySQL Backup Scripts. # Created by david. # # Created time: ...
- Linq to Oracle 使用教程(八)使用 T4 模版生成代码
点击这里返回目录 禁用原来的代码生成(可选步骤) 选择 Northwind.admf 文件,在属性窗口中,将 Custom Tool 属性设置为空,原来值为 ALinqCodeGenerator ...
最新文章
- Linux-C-Program:makefile
- 安装Exchange2003时出0XC1037AE6错误的解决方法.
- 论文阅读:Network In Network
- 《剑指offer》第九题(用两个栈实现队列)
- 认证授权方案之授权揭秘 (上篇)
- 装修好的房子多久能住 入住需要注意什么?
- 推荐一款免费的SSH+sftp工具
- 牛客网项目里的数据表
- Python的pandas安装超级详细
- 美团 O2O 供应链系统架构设计解析
- 抑制广播风暴 各种发包
- ue小知识点 动画蓝图 ABP的 begin、init和Character的posses的先后
- AS SSD软件查看信息说明
- 页面级优化——icon图标显示方式
- 无名namespace
- 天龙八部网单服务器修改爆率,天龙八部网游单服务器修改资料.doc
- 华为云首次突发大面积故障,网友哀嚎一片
- Java面试题及答案2019版(上),springboot缓存技术
- 计算机农业类的sci,农林类容易录用的sci期刊有哪些?
- 计算机类一节课的课程教学设计,《计算机应用基础》课程整体教学设计.pdf
热门文章
- jira软件 linux 安装,JIRA使用教程:在Linux上安装JIRA
- 信息学奥赛一本通 1227:Ride to Office | OpenJudge NOI 4.6 2404:Ride to Office
- 信息学奥赛一本通(1240:查找最接近的元素)
- 棋盘游戏(信息学奥赛一本通-T1451)
- 图论 —— AOE 网与关键路径
- 统计难题(HDU-1251)
- Maximum sum(信息学奥赛一本通-T1305)
- 11 CO配置-控制-成本中心会计-定义分割结构
- access文本框如何分开_ACCESS 2007 如何在窗体中将一个文本框的内容复制给另外一个文本框?...
- python多线程爬虫数据顺序_Python爬虫必学知识点:多线程爬虫