T4学习- 1、简介

一、T4简介

T4(Text Template Transformation Toolkit)在 Visual Studio 中,“T4 文本模板”是由一些文本块和控制逻辑组成的混合模板,它可以生成文本文件。

生成的文件可以是任何类型的文本,例如网页、资源文件或任何语言的程序源代码。

2012021410005097[1]

T4 文本模板有两种类型:

(1)、设计时模板

在 Visual Studio 中执行设计时 T4 文本模板,以便定义应用程序的部分源代码和其他资源。

通常您可以使用读取单个输入文件或数据库中的数据的多个模板,并生成一些 .cs、.vb 或其他源文件。

每个模板都生成一个文件。 在 Visual Studio 或 MSBuild 内执行它们。

若要创建设计时模板,请向您的项目中添加“文本模板”文件。 另外,您还可以添加纯文本文件并将其“自定义工具”属性设置为“TextTemplatingFileGenerator”。

(2)、运行时模板

可在应用程序中执行运行时 T4 文本模板(“预处理过的”模板)以便生成文本字符串(通常作为其输出的一部分)。

若要创建运行时模板,请向您的项目中添加“已预处理的文本模板”文件。另外,您还可以添加纯文本文件并将其“自定义工具”属性设置为“TextTemplatingFilePreprocessor”。

其实这里的运行时模板,就是这样一个工具,设定好模板后,然后VS自动转为C#代码,在部分类中TransformText()方法,我们可以找到踪迹。

运行时模板,简单的说就是可视化的C#代码生成器。运行时模板就好比C#代码,比起设计时模板,方便跟踪调试。

二、设计时模板T4例子--HelloWorld

1、选择“文本模板

新建项目名为:T4Sample,添加新项,选择“文本模板”即可创建设计时模板

20120215144731301_thumb

模板属性,请确认:

  • 自定义工具 = TextTemplatingFileGenerator
  • 生成操作 = 无

image_thumb

打开模板,默认下面几行语句

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".txt" #>

2、修改模板

依然以HelloWorld为例

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>

using System;

namespace Test
{
    public class HelloWorld
    { 
        public static void Main(string[] args)
        {
            <# 
               List<Person> people = GetPersonList();
               foreach(Person p in people)
              {
            #>
              Console.WriteLine("Hello {0},Welcome to T4 World!","<#= p.Name #>");
            <# }
            #>
        }
    }
}

<#+
//
public class Person
{
    ///名称
    public string Name{ get; set; }

    public Person(string name)
    {
        this.Name = name;
    }
}
//初始化众人
public List<Person> GetPersonList()
{
    List<Person> people = new List<Person>();

    Person p1 = new Person("Tom");
    Person p2 = new Person("Jim");
    Person p3 = new Person("Lucy");

    people.Add(p1);
    people.Add(p2);
    people.Add(p3);

    return people;
}

#>

3、生成代码

可通过以下方式

  • 保存,自动转换模板。
  • 或者右击模板文件,选择“运行自定义工具”
  • 或者在“解决方案资源管理器”工具栏中单击“转换所有模板”。 这将转换 Visual Studio 解决方案中的所有模板。

生成代码如下:

using System;

namespace Test
{
    public class HelloWorld
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello {0},Welcome to T4 World!", "Tom");
            Console.WriteLine("Hello {0},Welcome to T4 World!", "Jim");
            Console.WriteLine("Hello {0},Welcome to T4 World!", "Lucy");
        }
    }
}

4、调试T4模板

你可以在T4文本模板中设置断点。

要调试的设计时文本模板,将文本模板文件,保存,然后选择解决方案资源管理器中的文件的快捷菜单上的“调试 T4 模板”。

image_thumb1

2、

image_thumb2

5、实例2:代码生成器

该实例,就是我们通常见过的,模型代码生成器,根据数据模型生成代码框架,如模型层、数据访问层等。本实例先根据数据表,生存模型层代码。这是一个简单实例,如果有功夫,您可以根据这个原理写出自己的代码生成框架。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<# 
ModelManager manager = new ModelManager();
string tableName = "Dictionary";
DataTable table= manager.GetTableSchema(tableName);
#>
using System;

namespace Model
{
    public class <#= tableName #>
    {
        <#
        foreach(DataRow row in table.Rows)
        {
        #>
        public <#= manager.TransFromSqlType(row["DataType"].ToString())#> <#=row["ColumnName"]#>{ get; set; }

        <#}
        #>
    }
}

<#+
    public class ModelManager
    {
        /// <summary>
        /// 获得数据连接
        /// </summary>
        /// <returns></returns>
        private SqlConnection GetConnection()
        {
            return new SqlConnection("server=10.126.124.0;database=ZSOtherData;uid=it_zs;pwd=dev4ZS@2018;");
        }
 

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableName"></param>
        public DataTable GetTableSchema(string tableName)
        {
            DataTable dt;
            using (SqlConnection con = GetConnection())
            {
                con.Open();

                SqlCommand cmd = con.CreateCommand();
                cmd.CommandText = string.Format(@"SELECT  TABLE_NAME AS TableName  ,
                                                                    ORDINAL_POSITION AS ColumnID ,
                                                                    COLUMN_NAME AS ColumnName ,
                                                                    DATA_TYPE AS DataType ,
                                                                    CASE WHEN IS_NULLABLE = 'NO' THEN 'false'
                                                                         ELSE 'true'
                                                                    END AS IsNullable
                                                            FROM    INFORMATION_SCHEMA.COLUMNS AS A
                                                                    LEFT JOIN sysobjects AS B ON A.TABLE_NAME = B.name
                                                            WHERE   A.TABLE_NAME = '{0}'",tableName);
                cmd.CommandType = CommandType.Text;

                SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet(); 
                adapter.Fill(ds);
                dt = ds.Tables[0];
            }

            return dt;
        }
        /// <summary>
        /// SQL
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public string TransFromSqlType(string type)
        {
            if (string.IsNullOrEmpty(type))
            {
                return string.Empty;
            }

            if (string.Equals(type, "int", StringComparison.OrdinalIgnoreCase))
            {
                return "int";
            }
            else if (string.Equals(type, "varchar", StringComparison.OrdinalIgnoreCase))
            {
                return "string";
            }

            return "string";
        }

    }
#>

三、运行时模板T4例子

1、在解决方案资源管理器中,右击项目,指向“添加”,再单击“新建项”。

2、在“添加新项”对话框中,选择“运行时文本模板”。 (在 Visual Basic 中的“常用项\常规”下查看。)

image

3、键入模板文件的名称,如:MyWebPage
4、单击“添加”。将创建一个扩展名为 .tt 的新文件。 该文件的“自定义工具”属性设置为 TextTemplatingFilePreprocessor。

image

模板转换:
当然设计时模板可以转为运行时模板,只要将该文件的“自定义工具”属性设置为 TextTemplatingFilePreprocessor即可。
但是运行时模板很多情况下不能转为设计时模板。

新建模板后,自动生成了模板的分部类文件。

可以展开 .tt 文件节点,此附属文件包含一个分部类,该类包含一个名为 TransformText() 的方法。这个方法中的内容会根据模板内容的变化而变化,其实就是模板代码转为C#代码(不要手动更改)。此方法可以从应用程序中调用。

image

我们另外新建一个此分部类的文件,以模板名+code结尾:MyWebPageCode.cs,在这里我们可以声明模板需要的变量、方法

这里我们需要设置课程集合。

using System.Collections.Generic;

namespace T4Sample
{
    partial class MyWebPage
    {
        private List<string> items;

        public MyWebPage(List<string> data)
        {
            this.items = data;
        }
    }
}

修改模板:

<#@ template language="C#" #>
<html>
<body>
<h1>计算机课程</h2>
<table>
    <# foreach(string item in Items)
       { #>
         <tr><td>Course name : <#= item #> </td></tr>
    <# } #>
 </table>
 </body>
 </html>

查看附属文件的部分类,方法:TransformText()

public virtual string TransformText()
        {
            this.GenerationEnvironment = null;
            this.Write("<html>\r\n<body>\r\n<h1>计算机课程</h2>\r\n<table>\r\n    ");
            
            #line 6 "D:\Code\C#\UFIDA.U8.UAP.EntityGenerator\PreTemplateTest\MyWebPage.tt"
 foreach(string item in Items)
       { 
            
            #line default
            #line hidden
            this.Write("         <tr><td>Course name : ");
            
            #line 8 "D:\Code\C#\UFIDA.U8.UAP.EntityGenerator\PreTemplateTest\MyWebPage.tt"
            this.Write(this.ToStringHelper.ToStringWithCulture(item));
            
            #line default
            #line hidden
            this.Write(" </td></tr>\r\n    ");
            
            #line 9 "D:\Code\C#\UFIDA.U8.UAP.EntityGenerator\PreTemplateTest\MyWebPage.tt"
 } 
            
            #line default
            #line hidden
            this.Write(" </table>\r\n </body>\r\n </html>");
            return this.GenerationEnvironment.ToString();
        }

程序调用,对模板变量赋值,并调用模板,输出文件。

static void Main(string[] args)
        {
            List<string> items = new List<string>();
            items.Add("计算机基础");
            items.Add("数据库");
            items.Add("计算机网络");
            MyWebPage page = new MyWebPage(items);
            String pageContent = page.TransformText();
            //如果有汉字,需要设置编码格式
            System.IO.File.WriteAllText("outputPage.html", pageContent,Encoding.UTF8);
        }

运行程序,生成模板:

生成的HTML

<html><body>
<h1>计算机课程</h2>
<table>
             <tr><td>Course name : 计算机基础 </td></tr>
             <tr><td>Course name : 数据库 </td></tr>
             <tr><td>Course name : 计算机网络 </td></tr>
     </table>
 </body></html>

浏览器打开效果:

2012022716005065[1]

在运行时生成文本,若要在特定命名空间中放置已生成的类,请设置文本模板文件的“自定义工具命名空间”属性。

四、T4模板的基本结构

代码块的总体分类,就是两种:文本、程序脚本

  • 文本:就是需要生成的文本
  • 程序脚本:内部执行,最终生成想要的文本。T4中<# #>中的部分都属于程序脚本内容。

为了细分语法,方便大家更好的理解,用“块”(Block)来表示构成T4模板的基本单元,它们基本上可以分成5类:指令块(Directive Block)、文本块(Text Block)、代码语句块(Statement Block)、表达式块(Expression Block)和类特性块(Class Feature Block)。

1、指令块(Directive Block)

和ASP.NET页面的指令一样,它们出现在文件头,通过<#@…#>表示。其中<#@ template …#>指令是必须的,用于定义模板的基本属性,比如编程语言、基于的文化、是否支持调式等等。

比较常用的指令还包括用于程序集引用的<#@ assembly…#>,用于导入命名空间的<#@ import…#>等等。

指令通常是模板文件或包含的文件中的第一个元素。不应将它们放置在代码块 <#...#> 内,也不应放置在类功能块 <#+...#> 之后。

  • T4 模板指令
    <#@ template [language="VB"] [hostspecific="true"] [debug="true"] [inherits="templateBaseClass"] [culture="code"] [compilerOptions="options"] #>
  • T4 参数指令
    <#@ parameter type="Full.TypeName"name="ParameterName"#>
  • T4 输出指令
    <#@ output extension=".fileNameExtension"[encoding="encoding"] #>
  • T4 程序集指令
    <#@ assembly name="[assembly strong name|assembly file name]"#>
  • T4 导入指令
    <#@ import namespace="namespace"#>
  • T4 包含指令
    <#@ include file="filePath"#>

2、文本块(Text Block)

文本块就是直接原样输出的静态文本,不需要添加任何的标签。

3、代码语句块(Statement Block)

代码语句块通过<# 语句 #>的形式表示,中间是一段通过相应编程语言编写的程序调用,我们可以通过代码语句快控制文本转化的流程。

在上面的代码中,我们通过代码语句块实现对一个数组进行遍历,输出重复的Console.WriteLine("Hello {0},Welcome to T4 World!","<#= p.Name #>");语句。

4、表达式块(Expression Block)

表达式块以<#= 表达式 #>的形式表示,通过它之动态的解析的字符串表达内嵌到输出的文本中。比如在上面的foreach循环中,每次迭代输出的人名就是通过表达式块的形式定义的(<#=  p.Name #>)

5、类特性块(Class Feature Block)

如果文本转化需要一些比较复杂的逻辑,我们需要写在一个单独的辅助方法中,甚至是定义一些单独的类,我们就是将它们定义在类特性块中。

类特性块的表现形式为<#+ 特性快 #>对于Hello World模板,得到人名列表的GetPersonList方法就定义在类特性块中。

<#@ template language="C#" #>
<# HelloWorld(); #>
<#+
    private string _field = "classy";
    private void HelloWorld()
    {
        for(int i = 1; i <= 3; i++)
        {
#>
           Hello <#=_field#> World <#= i #>!
<#+
        }
    }
#>

五、T4模板语言编辑工具:

下载地址:tangible T4 Editor plus modelling tools

http://t4-editor.tangible-engineering.com/Download_T4Editor_Plus_ModelingTools.html

或者

https://www.devart.com/t4-editor/download.html

只需要简单的填一下,就OK了,是免费的哦。

主要的功能:

  1. 语法高亮显示
  2. T4中的指示符(directives)和代码快(code block)智能提示
  3. 对模板代码的智能提示
  4. 支持T4包含文件

image

六.T4的工作原理

如下

2012011212202287[1]

第一步,T4引擎编译这个模板文件:它会解析指令处理文件(也就是@开头部分)、文本文件和代码块,然后生成一个具体的TextTransformation文件,接着会把这个类文件编译成.NET的程序集。

第二步,T4引擎会创建一个GeneratedTextTransformation 类的实例。其中TransformText的方法,这个方法的主要作用就是用来保存输出文件中放回的字符串。

模板内容:

<#@ template language="C#" #>
<#
    for(int i = 1; i <= 3; i++)
    {
#>
Hello World <#= i #>!
<#
    }
#>

请注意这个:<#= i #>

后台编译的内容:

using System;
using Microsoft.VisualStudio.TextTemplating;  
 
namespace Microsoft.VisualStudio.TextTemplating76E036EA7C70CB236
{
   public class GeneratedTextTransformation: TextTransformation
   {
      public override string TransformText()
      {
         for (int i = 1; i <= 3; i++)
         {
            this.Write("Hello World ");
            this.Write(ToStringHelper.ToStringWithCulture(i));
            this.Write("!\r\n");
         }
         return this.GenerationEnvironment.ToString();
       }
   }
}

输出的内容就是:

Hello World 1!
Hello World 2!
Hello World 3!

自定义宿主:

https://msdn.microsoft.com/zh-cn/library/bb126579.aspx

posted on 2019-02-11 17:09  springsnow  阅读(545)  评论(0编辑  收藏  举报

导航