在《基于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模板生成代码?[上篇]相关推荐

  1. 创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]

    在<上篇>中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加.修改和删除操作的存储过程.但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板.如果 ...

  2. t4b代码生成_Ef+T4模板实现代码快速生成器

    效果如图,demo(点击demo可下载案例) 项目结构如图 T4BLL添加BLL.tt文件: T4Model添加Model文件: T4DAL添加DAL.tt文件: T4DAL 添加ADO.NET En ...

  3. t4模板生成html,强大的代码生成器——T4模板

    T4 Editor工具下载地址 tangible T4 Editor 2.5.0 plus modeling tools for VS 2019 https://marketplace.visuals ...

  4. [转]MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:Hello ...

  5. 使用 Source Generator 代替 T4 动态生成代码

    使用 Source Generator 代替 T4 动态生成代码 Intro 在 Source Generator 出现之前有一些重复性的代码,我会使用 T4 去生成,这样就可以一定程度上避免复制粘贴 ...

  6. 在Visual Studio中使用T4 Templates 生成代码

    在没有看过Hilton Giesenow(How Do I: Create and Use T4 Templates.)的视频之前,我还没意识到在Visual Studio 2008 中使用T4是何等 ...

  7. 使用Runnable接口创建线程,很简单

    大家好,今天分享.使用Runnable接口创建线程 首先Java创建线程可以通过三种方法: 即: 1.继承Thread类创建线程类(重点) 2.通过Runnable接口创建线程类(重点) 3.通过Ca ...

  8. mysql+代码备份,一个很简单的MYSQL数据库备份脚本代码

    假设有三个库 m_site,m_bbs,m_cms. #!/bin/sh # # MySQL Backup Scripts. # Created by david. # # Created time: ...

  9. Linq to Oracle 使用教程(八)使用 T4 模版生成代码

    点击这里返回目录   禁用原来的代码生成(可选步骤) 选择 Northwind.admf 文件,在属性窗口中,将 Custom Tool 属性设置为空,原来值为 ALinqCodeGenerator ...

最新文章

  1. Linux-C-Program:makefile
  2. 安装Exchange2003时出0XC1037AE6错误的解决方法.
  3. 论文阅读:Network In Network
  4. 《剑指offer》第九题(用两个栈实现队列)
  5. 认证授权方案之授权揭秘 (上篇)
  6. 装修好的房子多久能住 入住需要注意什么?
  7. 推荐一款免费的SSH+sftp工具
  8. 牛客网项目里的数据表
  9. Python的pandas安装超级详细
  10. 美团 O2O 供应链系统架构设计解析
  11. 抑制广播风暴 各种发包
  12. ue小知识点 动画蓝图 ABP的 begin、init和Character的posses的先后
  13. AS SSD软件查看信息说明
  14. 页面级优化——icon图标显示方式
  15. 无名namespace
  16. 天龙八部网单服务器修改爆率,天龙八部网游单服务器修改资料.doc
  17. 华为云首次突发大面积故障,网友哀嚎一片
  18. Java面试题及答案2019版(上),springboot缓存技术
  19. 计算机农业类的sci,农林类容易录用的sci期刊有哪些?
  20. 计算机类一节课的课程教学设计,《计算机应用基础》课程整体教学设计.pdf

热门文章

  1. jira软件 linux 安装,JIRA使用教程:在Linux上安装JIRA
  2. 信息学奥赛一本通 1227:Ride to Office | OpenJudge NOI 4.6 2404:Ride to Office
  3. 信息学奥赛一本通(1240:查找最接近的元素)
  4. 棋盘游戏(信息学奥赛一本通-T1451)
  5. 图论 —— AOE 网与关键路径
  6. 统计难题(HDU-1251)
  7. Maximum sum(信息学奥赛一本通-T1305)
  8. 11 CO配置-控制-成本中心会计-定义分割结构
  9. access文本框如何分开_ACCESS 2007 如何在窗体中将一个文本框的内容复制给另外一个文本框?...
  10. python多线程爬虫数据顺序_Python爬虫必学知识点:多线程爬虫