一、為什么叫T4模板
T4(Text Template Transformation Toolkit)是微軟官方在Visual Studio 2008中開始使用的代碼生成引擎。在Visual Studio中,"T4文本模板"是由一些文本塊和控制邏輯組成的混合模板,它可以生成文本文件。在Visual C#或Visual Basic中,控制邏輯編寫為程序代碼的片段。生成的文件可以是任何類型的文本,例如網頁、資源文件或任何語言的程序源代碼。現在的VS中只要與代碼生成相關的場景基本上都能找T4的身影,比如MVC的視圖模板,Entity Framework的DataContext模板等等。
二、模板的兩種類型
1、設計時模板
在Visual Studio中執行設計時T4文本模板,以便定義應用程序的部分源代碼和其他資源。
通常,您可以使用讀取單個輸入文件或數據庫中的數據的多個模板,並生成一些.cs、.vb或其他源文件。每個模板都生成一個文件。在Visual Studio或MSBuild內執行它們。
若要創建設計時模板,請向您的項目中添加“文本模板”文件。另外,您還可以添加純文本文件並將其“自定義工具”屬性設置為“TextTemplatingFileGenerator”。
2、運行時模板
可在應用程序中執行運行時T4文本模板(“預處理過的”模板)以便生成文本字符串(通常作為其輸出的一部分)。
若要創建運行時模板,請向您的項目中添加“已預處理的文本模板”文件。另外,您還可以添加純文本文件並將其“自定義工具”屬性設置為“TextTemplatingFilePreprocessor”
三、文本模板的組成
3.1 指令
1.T4模板指令
<#@ template {language="C#"} {debug="false"} {hostspecific="false"} {compilerOptions="options"} {culture="code"} {inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"} #>
1.模板指令中所有特性均為可選的。
2.language:輸出語言,有效值C#、VB,默認為C#。
3.debug:是否啟用測試,有效值true、false,默認值為false。特別說明下這個調試真的不咋地,很容易讓VS崩潰,很雞肋的功能。
4.hostspecific:有效值true、false,默認為false。如果將此特性的值設置為true,則會將名為Host的屬性添加到由文本模板生成的類中。該屬性是對轉換引擎的宿主的引用,並聲明為:Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost。
5.inherits:可以指定模板的程序代碼可以繼承自另一個類,這個類也可以從文本模板生成。目前沒有使用過,基本上可以忽略。
6.compilerOptions:有效值為任何有效的編譯器選項。基本上可以忽略
2.T4參數指令
<#@ parameter type="Full.TypeName" name="ParameterName" #>
顧名思義,就是用來傳參的,應該是用在運行時模板的(預處理模板)。
3.T4輸出指令
<#@ output extension=".txt" {encoding="utf-8"} #>
比較重要的指令,用於設置輸出文件的后綴名和文件編碼
extension:輸出文件擴展名,默認為“.cs”
encoding:文件編碼,默認為utf-8(這里不能確定,我測試是utf-8)。
4、T4 程序集指令
<#@ assembly name="[assembly strong name|assembly file name]" #>
1.程序集指令相當於VS里面我們添加程序集引用的功能,該指令只有一個參數name,用以指定程序集名稱,如果程序集已經在GAC里面注冊,那么只需要寫上程序集名稱即可,如<#@ assembly name="System.Data.dll" #>,否則需要指定程序集的物理路徑。
2.T4模板的程序集引用是完全獨立的,也就是說我們在項目中引用了一些程序集,然后項目中添加了一個T4模板,T4模板所需要的所有程序集引用必須明確的在模板中使用程序集執行引用才可以。
3.T4模板自動加載以下程序集Microsoft.VisualStudio.TextTemplating.1*.dll、System.dll、WindowBase.dll,如果用到了其它的程序集需要顯示的使用程序集添加引用才可以。
4.可以使用$(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。
5、T4導入指令
<#@ import namespace="System.Linq" #>
在Visual Studio T4文本模板的代碼塊中,import指令允許您在不提供完全限定名稱的情況下引用另一個命名空間中的元素。它等效於C#的using或visual Basic中的imports。默認已經導入了System命名空間的引用。
6、T4包含指令
<#@ include file="filePath" #>
1.filePath可以是絕對的或相對於當前的模板文件。
2.filePath可以包含用"%"分割的環境變量,例如:<#@ include file="%HOMEPATH%\MyIncludeFile.t4" #>
3.所包含的文件的名稱不必使用擴展名".tt"。可能需要針對包含的文件使用其他擴展名,例如:".t4"。這是因為,在您將.tt文件添加到項目中時,Visual Studio會自動將其“自定義工具”屬性設置為TextTemplatingFileGenerator。您通常不希望單獨轉換包含的文件。
4.在處理時,被包含內容就像是包含文本模板的組成部分一樣。不過,即使include指令后為普通文本塊和標准控制塊,也可以包括含有類功能塊<#+...#>的文件。
5.包含指令可以提高代碼復用率,比如我們可以將一些常用的程序集、命名空間引用放到一個文件里,使用時僅需要引用下即可,省去了每次都要重新引用一遍的煩惱,如我們建立Reference.ttinclude文件,里面包含了我們平時常用的程序集。
<#@ 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" #>
使用時只需要使用包含指令引用下即可
<#@ include file="$(ProjectDir)Reference.ttinclude" #>。
3.2 文本塊
文本塊直接向輸出文件插入文本。文本塊沒有特殊格式。例如:下面的文本模板將生成一個單詞“Hello World”
<#@ output extension=".txt" #>
Hello World!
3.3 控制塊
控制塊是用於轉換模板的程序代碼節。默認語言是C#,但若要使用Visual Basic,可以在文件開頭編寫以下指令:
<#@ template language="VB" #>
用於編寫控制塊代碼的語言與生成的文本的語言無關。
1.標准控制塊
標准控制塊是生成輸出文件部件的程序代碼節。
在模板文件中,可以混合使用任意數量的文本塊和標准控制塊。但是,不能再控制塊中嵌套控制塊。每個標准控制塊都以<#...#>符合分割。
例如,如果使用下面的控制塊和文本,則輸出文件包含行“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!
<#
}
#>
2.表達式控制塊
表達式控制塊計算表達式並將其轉換為字符串。該字符串將插入到輸出文件中。
表達式控制塊以<#=...#>符合分割。
例如,如果使用下面的控制塊,則輸出文件包含“5”:
<#= 2+3 #>
請注意:開始符號有三個字符"<#="。
表達式可以包含作用域中的任何變量。例如:下面的塊輸出數字行:
<#
for(int i=0;i<4;i++)
{
#>
This is hello number <#= i+1 #>:Hello!
<#
}
#>
輸出:
This is hello number 1:Hello!
This is hello number 2:Hello!
This is hello number 3:Hello!
This is hello number 4:Hello!
3.類功能控制塊
類功能控制塊定義屬性、方法或不應包含在主轉換中的所有其他代碼。類功能塊常用於編寫幫助器函數。通常,類功能塊位於單獨的文件中,這樣它們可以包含在多個文本模板中。
類功能控制塊以<#+...#>符號分割,可以簡單的認為<#+...#>定義的內容為我們的類文件。
示例程序
1、新建一個winform應用程序。
2、右鍵->添加->新建項目:選擇文本模板,更改名稱:PocoModel.tt。