示例代碼:示例代碼__你必須懂的T4模板:淺入深出.rar
?
(一)什么是T4模板?
T4,即4個T開頭的英文字母組合:Text Template Transformation Toolkit。
T4文本模板,即一種自定義規則的代碼生成器。根據業務模型可生成任何形式的文本文件或供程序調用的字符串。(模型以適合于應用程序域的形式包含信息,并且可以在應用程序的生存期更改)
?
VS本身只提供一套基于T4引擎的代碼生成的執行環境,由下面程序集構成:
Microsoft.VisualStudio.TextTemplating.10.0.dll
Microsoft.VisualStudio.TextTemplating.Interfaces.10.0.dll
Microsoft.VisualStudio.TextTemplating.Modeling.10.0.dll
Microsoft.VisualStudio.TextTemplating.VSHost.10.0.dll
?
便利工具:
1、??T4Toolbox.msi(CodePlex上開源的工具)
a)?????????提供一些可以直接使用的代碼生成器,比如Enum SQL View、AzMan wrapper、LINQ to SQL classes、LINQ to SQL schema和Entity Framework DAL等。
b)?????????提供一些基于T4方面的VS的擴展:當你安裝之后,在“Add New Item”對話框中就會多出一個命名為“Code Generation”的類別,其中包括若干文件模板。
2、??T4?模板編輯器(eg:支持代碼著色、智能提示)
a)?????????tangible T4 Editor?(下載)
b)?????????Visual T4?(下載)
??? 當然我們也可以通過VS2010中新增的擴展管理器(Extension Manager)來添加Vs擴展插件。擴展管理器(Extension Manager),這和Eclipse/Netbeans有些相似,用戶可以直接在IDE中從Visual Studio 庫(Visual Studio Gallery)找到并下載擴展。通過VS的菜單Tools->Extension Manager,這里你可以添加,刪除已經安裝的VS的擴展插件。打開界面如下:
?
?
筆者在學習?T4?的時候使用過上面兩個?T4?模板編輯器。稍作幾點對比:
a)?????????tangible T4 Editor可選擇安裝內嵌的?UML?模板模型
???????
b)?????????對于不是常用的dll( eg:EnvDTE.dll ),tangible T4 Editor免費版和?Visual T4?都不支持導航,并且所報的提示頁不一樣
tangible T4 Editor免費版中提示如下:
?
?
Visual T4中則直接提示:
?
但是在?Visual T4?中,我們可以通過在程序集中引入?EnvDTE.dll?解決此錯誤的提示(完成開發后可移除程序集引用),并且還能完美的獲得該程序集的智能提示功能,如下圖所示:
?
?????????同時我們也可以看到?Visual T4?中代碼著色也更加貼近?VS?(藍色字體標注對象)。
小結:
- 就“代碼著色”和“智能提示”方面Visual T4?工具表現更完美(前提是必須主動在項目中引入對應程序集),但目前最新版本存在縮進問題實在可惜,悲憤中等更新。.
- 可能你還想要tangible T4 Editor提供的?UML?模板模型,呵呵……現在我本機同時裝了這兩款?T4編輯器,暫時還沒發現沖突。
?
(二)T4基本結構
T4模板可以分為:指令塊、文本塊、控制塊。
- 指令塊?-?向文本模板化引擎提供關于如何生成轉換代碼和輸出文件的一般指令。
- 文本塊?-?直接復制到輸出的內容。
- 控制塊?-?向文本插入可變值并控制文本的條件或重復部件的程序代碼,不能在控制塊中嵌套控制塊。
n??指令塊
6個指令<#@ template #>、<#@ parameter#>、<#@ assembly #>、<#@ import #>、<#@ include #>、<#@ output #>、
其中,?output?和?assembly?只能用在設計時模板。
1)?????????T4?模板指令
<#@ template [language="C#"] [hostspecific="true"] [debug="true"] [culture="code"] [inherits="templateBaseClass"]?[compilerOptions="options"]?#>
這里只說明下?inherits?屬性,其余屬性在本文更合適的地方有進行說明。
inherits?????????????
指定模板的程序代碼繼承自另一個類,該基類可以是由其他模板生成。
1)?????????運行時(預處理過的)文本模板中的繼承
如果不指定?inherits?特性,則會從您的文本模板生成基類和派生類。指定?inherits?特性時,僅生成派生類。
2)?????????設計時文本模板中的繼承
設計時模板會生成任何類型的“文本文件”,此文件將組成?Visual Studio?項目的一部分。T4?模板引擎首先要將模板轉換為中間程序代碼文件,中間代碼文件將寫入您的?%TEMP% (環境變量)目錄。默認該生成的中間代碼繼承自?Microsoft.VisualStudio.TextTemplating.TextTransformation?類,但你也可根據需求使用?inherits?特性指定派生于?TextTransformation?類的任何基類。
???????????????????模板引擎生成轉換類更詳細的請參考本文后面的?何時編譯,編譯過程??節。
?
2)?????????T4?參數指令
<#@ parameter type="Full.TypeName" name="ParameterName" #>
在?Visual Studio?文本模板中,parameter?指令聲明模板代碼中從自外部上下文傳入的值初始化的屬性。可以聲明任何遠程類型的參數。也就是說,類型必須使用SerializableAttribute進行聲明,或者必須從MarshalByRefObject派生。這樣可以將參數值傳遞到在其中處理模板的AppDomain中。
如何使用及內部運作機制請查看我的另一篇文章?《(譯)理解?T4?模板:<#@ parameter #>?指令》?。
?
3)?????????T4?導入指令
<#@ import namespace="namespace" #>
?
4)?????????T4?包含指令
<#@ include file="filePath" #>
a)?????????為了增加代碼的可維護性,將公用函數做為類功能塊(<#+?類功能控制塊?#>)存放在單獨的文件中,該文件可以被?<#@include#>?到一個或多個模板文件中。
b)?????????對于包含文件,文件擴展名使用?.ttinclude可讀性更好。(以區分后綴為?.tt的運行時或設計時文本模板)
?
5)?????????T4?輸出指令
<#@ output extension=".fileNameExtension" [encoding="encoding"] #>
運行時(預處理)文本模板中不需要?output?指令。應用程序通過調用TextTransform()?來獲取已生成的字符串。
?
6)?????????T4?程序集指令
<#@ assembly name="[assembly strong name|assembly file name]" #>
在預處理文本模板中,assembly?指令無效。改為在?Visual Studio?項目中直接“添加引用”。
程序集名稱應為以下各項之一:
- GAC?中程序集的強名稱,例如?System.Xml.dll。還可以使用長名稱,例如?name="System.Xml, Version=4.0.0.0, Culture=neutral,PublicKeyToken=b77……"。
- 程序集的絕對路徑
可以使用?$(variableName)?語法引用?Visual Studio?或MSBuild變量(如?$(SolutionDir)),以及使用?%VariableName%?來引用環境變量。
???????????????????另,給出一些常用的?【生成命令和屬性的宏】
$(ConfigurationName) | 當前項目配置的名稱(如“Debug”)。 |
$(PlatformName) | 當前項目平臺的名稱(如“Win32”)。 |
$(ProjectName) | 項目的基本名稱。 |
$(TargetDir) | 生成的主輸出文件的目錄(定義為驅動器?+?路徑);包括尾部的反斜杠“\”。 |
$(TargetName) | 生成的主輸出文件的基本名稱。 |
$(FrameworkDir) | 安裝?.NET Framework?的目錄。 |
$(FrameworkVersion) | Visual Studio?使用的?.NET Framework?版本。 |
$(WebDeployPath) | 從?Web?部署根到項目輸出所屬于的位置的相對路徑。返回與RelativePath相同的值。 |
$(WebDeployRoot) | 指向<localhost>位置的絕對路徑。例如,c:\inetpub\wwwroot。 |
?
n??控制塊
有三種類型的控制塊,根據其左括號對它們進行區分:
1.??????<#?標準控制塊?#>????????????????????????????可以包含語句。
2.??????<#=?表達式控制塊?#>????????????將一個可以計算為字符串的表達式括起來,用于提供要寫入“輸出”文件的字符串的代碼。
3.??????<#+?類功能控制塊?#>????????????可以使用類功能控制塊向文本模板添加方法、屬性、字段甚至是嵌套類。必須作為文件中的最后一個塊顯示,或者用<#@ include #>引入外部文件。
注意:
1)?????????始終使用?{...}花括號來包含內嵌的嵌套語句,否則會報錯。(哪怕花括號中只有一句代碼)
2)?????????控制塊不能互相嵌套。必須先終止之前的控制塊,然后才能打開另一個。
?
(三)設計時模板和運行時模板
T4文本模板分為:設計時模板和運行時模板
n??添加模板
- 設計時模板(文本模板)
優勢:當需求變化時,可以根據業務需求調整模型(輸入),按照指定規則將“模型”生成任何類型的“文本文件”,例如:網頁、資源文件或任何語言的程序源代碼。(模型:是描述應用程序特定方面的數據源。它可以是任何形式、任何類型的文件或數據庫。如:數據庫、配置文件、UML?模型、DSL?模型或其他源)
a)?????????VS中新建文件——常規——文本模板。(如圖)
?
該模板文件中已包含下列指令:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
b)?????????或則,添加“純文本文件”并設置下圖屬性,加入相關指令。(后綴推薦改為標準的?*.tt)
設計時模板: TextTemplatingFileGenerator
?
- 運行時模板(已預處理的文本模板)???
優勢:當需求變化時,可以根據業務需求調整模型(輸入),在運行時按照指定規則將“模型”生成為“文本字符串”。
- VS中新建文件——常規——已預處理的文本模板。
該模板文件包含指令:<#@ template language="C#" #>
- 或則,添加“純文本文件”并設置相應屬性,加入相關指令。
運行時模板:TextTemplatingFilePreprocessor
?
n??何時編譯,編譯過程
- 何時編譯
在下列任何一種情況下,將執行模板,同時生成附屬文件,生成的文件將作為項目的一部分編譯。(屬性框----生成操作:編譯)
1)?????????編輯模板(模板有異動且沒有被保存),當前編輯模板失去焦點。
2)?????????保存模板。
3)?????????在“解決方案資源管理器”工具欄中單擊“轉換所有模板”。轉換解決方案中的所有模板。
??????
4)?????????右擊“解決方案資源管理器”中的一個或多個模板文件,然后選擇“運行自定義工具”。
- 編譯過程
設計時模板
1)?????????文本模板轉換引擎將“文本模板”轉換為可執行的cs代碼——“轉換類”。轉換類(*.cs)存于臨時目錄下。(臨時目錄在“環境變量”中設置:右鍵“我的電腦”—“屬性”—“高級系統設置”—“高級”選項卡中“環境變量”—TEMP變量)
命名空間:Microsoft.VisualStudio.TextTemplating +?隨機碼
基類:Microsoft.VisualStudio.TextTemplating.TextTransformation
類名:GeneratedTextTransformation
?
2)?????????引擎編譯生成的“轉換類”生成dll,dll存于臨時目錄下。具體是哪個dll可以在模板的“調試環境”下使用System.Reflection.Assembly.GetExecutingAssembly();獲取。
3)?????????執行已編譯的轉換類,生成“文件”。新文件會在“解決方案資源管理器”中出現在文本模板文件下。
?
運行時模板
1)?????????運行時模板沒有<#@ output #>指令,文本模板引擎將“運行時模板”直接編譯為cs文件,作為項目的一部分編譯。新文件會在“解決方案資源管理器”中出現在文本模板文件下。
命名空間:默認為所屬程序集的命名空間
基類:模板文件名?+ Base?
類名:模板文件名(PreTextTemplateTest.tt)——注意是“分部類”
?
2)?????????生成的代碼文件隨著項目一起編譯,并可在應用程序中通過調用生成類中的TransformText()?方法輸出“文本字符串”。
?
另外,若要在特定命名空間中放置模板轉換生成的類,需設置模板文件的“自定義工具命名空間”屬性。
?
- 注意事項
1)?????????控制塊使用陷進
TransformText()?方法是由模板引擎將模板中的所有“控制塊”代碼(包括“包含的模板”)組合生成。所以在使用控制塊時應注意以下幾點:
a)?????????語言:只能使用一種語言。
b)?????????局部變量:確保局部變量的名稱不會沖突。
2)?????????文本模板在單獨的AppDomain中運行
請注意,文本模板在與主應用程序分開的AppDomain中運行。在大多數情況下這并不重要,但在某些復雜的情況下您可能會發現一些限制。例如,如果要從單獨的服務將數據傳入模板或從中傳出數據,則該服務必須提供可序列化的?API。
?
(四)技巧
l??快速編寫模板
以生成文件為原型,然后逐步插入用于改變結果的控制塊。
?
l??T4文本模板的斷點調試
- 注冊表:設置DbgJITDebugLaunchSetting值為?2。
(x86系統): HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
(x64?系統): HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework
- 為template指令添加debug="true"特性:<#@ template debug="true"#>
- 命令:
<# System.Diagnostics.Debugger.Launch();#>??????????????????在模板執行到特定點啟動調試器。如果用Debugger.Break()啟動調試器在調試完后會導致?VS?奔潰。
<#System.Diagnostics.Debugger.Break();#>?????????????????????啟動調試器后,使用此命令在后續特定點處再次進入調試模式,相當于斷點。
使用方法:必須使用“Debugger.Launch()”命令啟動調試器(如下圖,啟動新實例或使用已存在的VS附加。注意,若此處點擊取消則將關閉當前IDE),調試完后可以不用中斷調試,不影響模板編輯,當再次編譯模板時如果存在“Debugger.Break()”命令則自動進入調試模式。
?
?
l??向模板傳遞參數的兩種方法
- 使用?<#@ parameter#>?指令引入參數,由模板引擎生成屬性訪問代碼。詳細請看?《(譯)理解?T4?模板:<#@ parameter #>?指令》?。
- 在構造函數中傳遞參數。只適用于運行時模板,此種模板生成的代碼以分部類的形式編寫。可以在項目的另一個文件中創建同一個類的其他部分,該文件可以包含一個帶參數的構造函數、若干屬性和函數,在調用?TransformText()?實例方法前進行初始化。
?
l??使用模板繼承共享內容
可以通過編寫基類模板(可以是抽象模板)在文本模板之間共享內容。使用<@#template#>?指令的?inherits?特性指定基類。
?
l??運行時調用設計時模板返回字符串
調用?Microsoft.VisualStudio.TextTemplating.Engine?的?ProcessTemplate?方法。
publicstring?ProcessTemplate(
?????string?content,
?????ITextTemplatingEngineHost host
)
????????content????參數指定文本模板的內容,eg: 使用System.IO.File.ReadAllText(Path)?進行讀取
host????????參數指定的宿主,必須是實現?ITextTemplatingEngineHost?的類。這是由模板引擎回調的。宿主必須能記錄錯誤、解析對程序集和包含文件的引用、提供可在其中執行模板的應用程序域并為每條指令調用相應的處理器。
?
演練:創建自定義文本模板宿主
?
(五)常用方法
n??模板基類提供的方法
設計時模板繼承TextTransformation抽象類
?
?
運行時模板默認繼承自動生成的基類
?
- Write()?和WriteLine()?方法
寫入目的輸出文本的三種方式:
a)?????????文本塊
b)?????????表達式控制塊:??????<#=?變量?#>
c)?????????標準控制塊:???????????<# Write() | WriteLine() #>,因為控制塊不能嵌套,所以此種方式比<#=?變量?#>書寫更優雅。
- 輸出文本縮進設置
可以使用縮進方法設置文本模板輸出的格式。
a)?????????PushIndent(string indent)?????????添加指定格式,內部會將字符長度加入到緩存變量indentLengths列表(List<int>)。
b)?????????PopIndent()?????????????????以“堆棧(先進后出)”形式移除格式,內部按indentLengths列表中存的字符長度進行移除。
c)?????????ClearIndent()??????????????刪除所有縮進。
注意:格式用完后要注意清除,否則可能出現模板中的空行會生成?Write(“\r\n”)?中間代碼,最終造成將縮進的格式錯誤輸出到了目的文件。
Eg:
?
- 錯誤報告
若要在?Visual Studio?錯誤窗口中放置錯誤消息和警告消息,可以使用以下方法:
<# this.Error("An error message"); #>
<# Warning("A warning message"); #>
?
n??使用執行模板的主機(例如?Visual Studio)公開的方法和屬性。這適用于常規文本模板,而不是預處理過的文本模板。
首先,給?template?指令添加hostspecific="true"?特性,以便使用this.Host對象。
(Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost)接口提供方法
?
- 使用this.Host.ResolvePath()從相對路徑名打開文件
- 使用LogErrors()?顯示錯誤消息,如下圖:
???????
- 使用?Visual Studio?中提供的服務(加載EnvDTE.dll?)
EnvDTE是組件包裝?COM?程式庫,其中包含了?Visual Studio?核心?Automation?的物件及成員。
引入?EnvDTE.dll?組件后應按下圖“屬性”進行設置:
?
示例:
<#@ assembly name="EnvDTE" #>
<#
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
EnvDTE.DTEdte = (EnvDTE.DTE) serviceProvider.GetService(typeof(EnvDTE.DTE));
dte.Solution.SaveAs("C:\\backup_Solution");
#>