額,T4好陌生的名字,和NuGet一樣很悲催,不為世人所熟知,卻又在背后默默無聞的奉獻着,直到現在我們項目組的人除了我之外,其它人還是對其豪無興趣,基本上是連看一眼都懶得看,可憐的娃啊。。。
T4(Text Template Transformation Toolkit)是微軟官方在VisualStudio 2008中開始使用的代碼生成引擎。在 Visual Studio 中,“T4 文本模板”是由一些文本塊和控制邏輯組成的混合模板,它可以生成文本文件。 在 Visual C# 或 Visual Basic 中,控制邏輯編寫為程序代碼的片段。生成的文件可以是任何類型的文本,例如網頁、資源文件或任何語言的程序源代碼。現在的VS中只要與代碼生成相關的場景基本上都能找T4的身影,比如MVC的視圖模板,Entity Framwork的DataContext模板等等。
在學習枯燥的概念前我們先來看一下用T4模版快速生成POCO實體類的示例
打開VS2010建一個項目,然后右擊項目文件選擇新建項,在文件列表中找到文件模版,如圖所示
修改TextTemplate1.tt文件內容如下
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class User { /// <summary> /// 用戶ID /// </summary> public int UserID { get; set; } /// <summary> /// 用戶名 /// </summary> public string UserName { get; set; } /// <summary> /// 密碼 /// </summary> public string Password { get; set; } /// <summary> /// Email /// </summary> public string Email { get; set; } /// <summary> /// 手機號 /// </summary> public string Mobile { get; set; } } }
點擊Ctrl+S然后可以看到自動生成了一個TextTemplate1.cs文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class User { /// <summary> /// 用戶ID /// </summary> public int UserID { get; set; } /// <summary> /// 用戶名 /// </summary> public string UserName { get; set; } /// <summary> /// 密碼 /// </summary> public string Password { get; set; } /// <summary> /// Email /// </summary> public string Email { get; set; } /// <summary> /// 手機號 /// </summary> public string Mobile { get; set; } } }
是不是很神奇,T4模版引擎會根據你在模版里定義的內容自動生成相應的文件,當然本示例過於簡單,完全不能展現T4的強大,當你若真正了解T4后,會發現神馬代碼生成器全都是浮雲,T4才是王道,利用T4你完全可以輕松生成屬於自己風格的任何類型代碼,在下篇文章會有實例展示如何通過T4連接數據庫自動生成POCO類,基本上這個才是我們用T4的最大意圖,呵呵,在這之前還是老老實實看看枯燥乏味的T4基礎知識吧。
T4 文本模板有兩種類型:設計時 T4 文本模板和運行時 T4 文本模板(“預處理過的”模板)
- 設計時模版
可在應用程序中執行運行時 T4 文本模板(“預處理過的”模板)以便生成文本字符串(通常作為其輸出的一部分)。
若要創建運行時模板,請向您的項目中添加“已預處理的文本模板”文件。 另外,您還可以添加純文本文件並將其“自定義工具”屬性設置為“TextTemplatingFilePreprocessor”。
有關更多信息,請參見使用預處理 T4 文本模板生成運行時文本。 有關模板語法的更多信息,請參見編寫 T4 文本模板。
- 運行時模版
在 Visual Studio 中執行設計時 T4 文本模板,以便定義應用程序的部分源代碼和其他資源。
通常,您可以使用讀取單個輸入文件或數據庫中的數據的多個模板,並生成一些 .cs、.vb 或其他源文件。 每個模板都生成一個文件。 在 Visual Studio 或 MSBuild 內執行它們。
若要創建設計時模板,請向您的項目中添加“文本模板”文件。 另外,您還可以添加純文本文件並將其“自定義工具”屬性設置為“TextTemplatingFileGenerator”。
有關更多信息,請參見使用 T4 文本模板生成設計時代碼。 有關模板語法的更多信息,請參見編寫 T4 文本模板。
文本模板由以下部件組成:
T4 文本模板指令
- T4模版指令
<#@ template [language="C#"] [compilerOptions="options"] [culture="code"] [debug="true"] [hostspecific="true"] [inherits="templateBaseClass"] #>
- 模版指令中所有特性均為可選的
- langeuage:輸出語言,有效值C#、VB,默認為C#
- debug:是否啟用調試,有效值true、false,默認為false。特別說明下這個調試真的不咋地,很容易讓VS崩潰,很雞肋的功能,
- hostspecific:有效值true、false,默認為false。如果將此特性的值設置為 true,則會將名為 Host 的屬性添加到由文本模板生成的類中。 該屬性是對轉換引擎的宿主的引用,並聲明為Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost。
- inherits:可以指定模板的程序代碼可以繼承自另一個類,這個類也可以從文本模板生成。目前木有使用過,基本上可以忽略
- compilerOptions:有效值為任何有效的編譯器選項。基本上可以忽略
- T4 參數指令
<#@ parameter type="Full.TypeName" name="ParameterName" #>
顧名思義,就是用來傳參的,應該是用在運行時模版的(預處理模版)
- T4 輸出指令
<#@ output extension=".fileNameExtension" [encoding="encoding"] #>
比較重要的指令,用於設置輸出文件的后綴名和文件編碼
extension:輸出文件擴展名,默認為".cs"
encoding:文件編碼,默值為utf-8(這里不能確定,我測試是utf-8)
- T4 程序集指令
<#@ assembly name="[assembly strong name|assembly file name]" #>
- 程序集指令相當於VS里面我們添加程序集引用的功能,該指令只有一個參數name,用以指定程序集名稱,如果程序集已經在GAC里面注冊,那么只需要寫上程序集名稱即可,如<#@ assembly name="System.Data.dll" #>,否則需要指定程序集的物理路徑。
- T4模版的程序集引用是完全獨立的,也就是說我們在項目中引用了一些程序集,然后項目中添加了一個T4模版,T4模版所需要的所有程序集引用必須明確的在模版中使用程序集執行引用才可以。
- T4模版自動加載以下程序集Microsoft.VisualStudio.TextTemplating.1*.dll、System.dll、WindowsBase.dll,如果用到了其它的程序集需要顯示的使用程序集添加引用才可以
- 可以使用 $(variableName) 語法引用 Visual Studio 或 MSBuild 變量(如 $(SolutionDir)),以及使用 %VariableName% 來引用環境變量。介紹幾個常用的$(variableName) 變量:
$(SolutionDir):當前項目所在解決方案目錄
$(ProjectDir):當前項目所在目錄
$(TargetPath):當前項目編譯輸出文件絕對路徑
$(TargetDir):當前項目編譯輸出目錄,即web項目的Bin目錄,控制台、類庫項目bin目錄下的debug或release目錄(取決於當前的編譯模式)
舉個例子:比如我們在D盤根目錄建立了一個控制台項目TestConsole,解決方案目錄為D:\LzrabbitRabbit,項目目錄為
D:\LzrabbitRabbit\TestConsole,那么此時在Debug編譯模式下
$(SolutionDir)的值為D:\LzrabbitRabbit
$(ProjectDir)的值為D:\LzrabbitRabbit\TestConsole
$(TargetPath)值為D:\LzrabbitRabbit\TestConsole\bin\Debug\TestConsole.exe
$(TargetDir)值為D:\LzrabbitRabbit\TestConsole\bin\Debug\
- T4 導入指令
<#@ import namespace="namespace" #>
在 Visual Studio T4 文本模板的代碼塊中,import 指令允許您在不提供完全限定名稱的情況下引用另一個命名空間中的元素。 它等效於 C# 中的 using 或 Visual Basic 中的 imports。默認已經導入了System命名空間的引用。
- T4 包含指令
<#@ include file="filePath" #>
- filePath 可以是絕對的,或相對於當前模板文件。
- filePath 可以包括用“%”分隔的環境變量。 例如:<#@ include file="%HOMEPATH%\MyIncludeFile.t4" #>
- 所包含的文件的名稱不必使用擴展名“.tt”。可能需要針對包含的文件使用其他擴展名,例如,“.t4”。 這是因為,在您將 .tt 文件添加到項目中時,Visual Studio 會自動將其“自定義工具”屬性設置為 TextTemplatingFileGenerator。 您通常不希望單獨轉換包含的文件。
- 在處理時,被包含內容就像是包含文本模板的組成部分一樣。 不過,即使 include 指令后為普通文本塊和標准控制塊,也可以包括含有類功能塊 <#+...#> 的文件。
- 包含指令可以提高代碼復用率,比如我們可以將一些常用的程序集、命名空間引用放到一個文件里,使用時僅需要引用下即可,省去了每次都要重新引用一遍的煩惱,如我們建立Reference.ttinclude文件,里面包含了我們平時常用的程序集引用
<#@ assembly name="System.Core.dll" #> <#@ assembly name="System.Data.dll" #> <#@ assembly name="System.Data.DataSetExtensions.dll" #> <#@ assembly name="System.Xml.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.Xml" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #>
使用時只需要使用包含指令引用下即可
<#@ include file="$(ProjectDir)Reference.ttinclude" #>
文本塊
文本塊直接向輸出文件插入文本。 文本塊沒有特殊格式。 例如,下面的文本模板將生成一個包含單詞“Hello World!”的文本文件:
<#@ output extension=".txt" #> Hello World!
控制塊
控制塊是用於轉換模板的程序代碼節。 默認語言是 C#,但若要使用 Visual Basic,可以在文件開頭編寫以下指令:
<#@ template language="VB" #>
用於編寫控制塊代碼的語言與生成的文本的語言無關。
標准控制塊
標准控制塊是生成輸出文件部件的程序代碼節。
在模板文件中,可以混合使用任意數量的文本塊和標准控制塊。 但是,不能在控制塊中嵌套控制塊。 每個標准控制塊都以 <# ... #> 符號分隔。
例如,如果使用下面的控制塊和文本塊,則輸出文件包含行“0, 1, 2, 3, 4 Hello!”:
<# for(int i = 0; i < 4; i++) { Write(i + ", "); } Write("4"); #> Hello!
您可以交錯文本和代碼,而不必使用顯式 Write() 語句。 以下示例輸出“Hello!”四次:
<# for(int i = 0; i < 4; i++) { #> Hello! <# } #>
在代碼中,可以使用 Write(); 語句的位置都可以插入文本塊。
表達式控制塊
表達式控制塊計算表達式並將其轉換為字符串。 該字符串將插入到輸出文件中。
表達式控制塊以 <#= ... #> 符號分隔。
例如,如果使用下面的控制塊,則輸出文件包含“5”:
<#= 2 + 3 #>
請注意,開始符號有三個字符“<#=”。
表達式可以包含作用域中的任何變量。 例如,下面的塊輸出數字行:
<#@ output extension=".txt" #> <# for(int i = 0; i < 4; i++) { #> This is hello number <#= i+1 #>: Hello! <# } #>
類功能控制塊
類功能控制塊定義屬性、方法或不應包含在主轉換中的所有其他代碼。 類功能塊常用於編寫幫助器函數。 通常,類功能塊位於單獨的文件中,這樣它們可以包含在多個文本模板中。
類功能控制塊以 <#+ ... #> 符號分隔,可以簡單的認為<#+ ...#>定義的內容為我們的類文件
例如,下面的模板文件聲明並使用一個方法:
<#@ output extension=".txt" #> Squares: <# for(int i = 0; i < 4; i++) { #> The square of <#= i #> is <#= Square(i+1) #>. <# } #> That is the end of the list. <#+ // Start of class feature block private int Square(int i) { return i*i; } #>
類功能必須編寫在文件末尾。 不過,即使 include 指令后跟標准塊和文本,也可以 <#@include#> 包含類功能的文件。
類功能塊可以包含文本塊
可以編寫生成文本的方法。 例如:
List of Squares:
<# for(int i = 0; i < 4; i++) { WriteSquareLine(i); } #> End of list. <#+ // Class feature block private void WriteSquareLine(int i) { #> The square of <#= i #> is <#= i*i #>. <# } #>
將文本生成方法放置在可供多個模板包含的單獨文件中,是非常有用的。
嗯,大概的基礎點應該就這些了,更多的自己去MSDNhttp://msdn.microsoft.com/zh-cn/library/bb126445詳細了解吧
這里解釋點容易讓人困惑的地方,我們在T4模版里面引用的程序集和那些命名空間都是利用T4生成代碼需要使用的,也就是T4模版要用的,和我們要生成的目標類毛關系都沒有,當初為搞清楚這點可是費了不精力。T4初看起來很復雜,其實稍微花些心思研究下,主要似乎把MSDN看懂還是很容易學會的,一旦掌握了受用無窮