(一)什么是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上開源的工具)
vs2017中,新增項已經默認有Generation,可以直接添加“運行時文本模板”
官網:http://olegsych.com/T4Toolbox/
GitHub:https://github.com/olegsych/T4Toolbox
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:支持代碼着色、智能提示)
工具下載: tangible T4 Editor (下載)
免費版本已經提供簡單的“代碼着色”、“智能提示”以及內嵌的UML模型圖,收費版本有提供更加豐富的功能。
通過VS的菜單Tools->Extension Manager,這里你可以添加,刪除已經安裝的VS的擴展插件。
添加新項中,可以看到
另外:修改 T4 editor 默認背景色
(二)T4基本結構
T4模板可以分為:指令塊、文本塊、控制塊。
1. 指令塊 - 向文本模板化引擎提供關於如何生成轉換代碼和輸出文件的一般指令。
2. 文本塊 - 直接復制到輸出的內容。
3. 控制塊 - 向文本插入可變值並控制文本的條件或重復部件的程序代碼,不能在控制塊中嵌套控制塊。
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 項目中直接“添加引用”。
程序集名稱應為以下各項之一:
1. GAC 中程序集的強名稱,例如 System.Xml.dll。還可以使用長名稱,例如 name="System.Xml, Version=4.0.0.0, Culture=neutral,PublicKeyToken=b77……"。
2. 程序集的絕對路徑
可以使用 $(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 添加模板
1. 設計時模板(文本模板)
優勢:當需求變化時,可以根據業務需求調整模型(輸入),按照指定規則將“模型”生成任何類型的“文本文件”,例如:網頁、資源文件或任何語言的程序源代碼。(模型:是描述應用程序特定方面的數據源。它可以是任何形式、任何類型的文件或數據庫。如:數據庫、配置文件、UML 模型、DSL 模型或其他源)
a) VS中新建文件——常規——文本模板。(如圖)
該模板文件中已包含下列指令:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
b) 或則,添加“純文本文件”並設置下圖屬性,加入相關指令。(后綴推薦改為標准的 *.tt)
設計時模板: TextTemplatingFileGenerator
2. 運行時模板(已預處理的文本模板)
優勢:當需求變化時,可以根據業務需求調整模型(輸入),在運行時按照指定規則將“模型”生成為“文本字符串”。
1. VS中新建文件——常規——已預處理的文本模板。
該模板文件包含指令:<#@ template language="C#" #>
2. 或則,添加“純文本文件”並設置相應屬性,加入相關指令。
運行時模板:TextTemplatingFilePreprocessor
在下列任何一種情況下,將執行模板,同時生成附屬文件,生成的文件將作為項目的一部分編譯。(屬性框----生成操作:編譯)
1) 編輯模板(模板有異動且沒有被保存),當前編輯模板失去焦點。
2) 保存模板。
3) 在“解決方案資源管理器”工具欄中單擊“轉換所有模板”。轉換解決方案中的所有模板。
4) 右擊“解決方案資源管理器”中的一個或多個模板文件,然后選擇“運行自定義工具”。
2. 編譯過程
設計時模板
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() 方法輸出“文本字符串”。
另外,若要在特定命名空間中放置模板轉換生成的類,需設置模板文件的“自定義工具命名空間”屬性。
3. 注意事項
1) 控制塊使用陷進
TransformText() 方法是由模板引擎將模板中的所有“控制塊”代碼(包括“包含的模板”)組合生成。所以在使用控制塊時應注意以下幾點:
a) 語言:只能使用一種語言。
b) 局部變量:確保局部變量的名稱不會沖突。
2) 文本模板在單獨的AppDomain中運行
請注意,文本模板在與主應用程序分開的AppDomain中運行。在大多數情況下這並不重要,但在某些復雜的情況下您可能會發現一些限制。例如,如果要從單獨的服務將數據傳入模板或從中傳出數據,則該服務必須提供可序列化的 API。
(四)技巧
l 快速編寫模板
以生成文件為原型,然后逐步插入用於改變結果的控制塊。
l T4文本模板的斷點調試
1. 注冊表:設置DbgJITDebugLaunchSetting值為 2。
(x86系統): HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
(x64 系統): HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework
2. 為template指令添加debug="true"特性:<#@ template debug="true"#>
3. 命令:
<# System.Diagnostics.Debugger.Launch();#> 在模板執行到特定點啟動調試器。如果用Debugger.Break()啟動調試器在調試完后會導致 VS 奔潰。
<#System.Diagnostics.Debugger.Break();#> 啟動調試器后,使用此命令在后續特定點處再次進入調試模式,相當於斷點。
使用方法:必須使用“Debugger.Launch()”命令啟動調試器(如下圖,啟動新實例或使用已存在的VS附加。注意,若此處點擊取消則將關閉當前IDE),調試完后可以不用中斷調試,不影響模板編輯,當再次編譯模板時如果存在“Debugger.Break()”命令則自動進入調試模式。
l 向模板傳遞參數的兩種方法
1. 使用 <#@ parameter#> 指令引入參數,由模板引擎生成屬性訪問代碼。詳細請看 《(譯)理解 T4 模板:<#@ parameter #> 指令》 。
2. 在構造函數中傳遞參數。只適用於運行時模板,此種模板生成的代碼以分部類的形式編寫。可以在項目的另一個文件中創建同一個類的其他部分,該文件可以包含一個帶參數的構造函數、若干屬性和函數,在調用 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抽象類
運行時模板默認繼承自動生成的基類
1. Write() 和WriteLine() 方法
寫入目的輸出文本的三種方式:
a) 文本塊
b) 表達式控制塊: <#= 變量 #>
c) 標准控制塊: <# Write() | WriteLine() #>,因為控制塊不能嵌套,所以此種方式比<#= 變量 #>書寫更優雅。
2. 輸出文本縮進設置
可以使用縮進方法設置文本模板輸出的格式。
a) PushIndent(string indent) 添加指定格式,內部會將字符長度加入到緩存變量indentLengths列表(List<int>)。
b) PopIndent() 以“堆棧(先進后出)”形式移除格式,內部按indentLengths列表中存的字符長度進行移除。
c) ClearIndent() 刪除所有縮進。
注意:格式用完后要注意清除,否則可能出現模板中的空行會生成 Write(“\r\n”) 中間代碼,最終造成將縮進的格式錯誤輸出到了目的文件。
Eg:
3. 錯誤報告
若要在 Visual Studio 錯誤窗口中放置錯誤消息和警告消息,可以使用以下方法:
<# this.Error("An error message"); #>
<# Warning("A warning message"); #>
n 使用執行模板的主機(例如 Visual Studio)公開的方法和屬性。這適用於常規文本模板,而不是預處理過的文本模板。
首先,給 template 指令添加hostspecific="true" 特性,以便使用this.Host對象。
(Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost)接口提供方法
1. 使用this.Host.ResolvePath()從相對路徑名打開文件
2. 使用LogErrors() 顯示錯誤消息,如下圖:
3. 使用 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");
#>
技巧
1、T4生成多個文件的方法,因為文本模板技術不支持生成多個文件
方法一、使用 EF.Utility.CS.ttinclude (問題:此方式不支持在netstandard項目或net core項目中使用,一使用vs就卡死)
方式二、Multiple outputs from T4 made easy – revisited (github 進入)
2、使用T4生成 Entity、IRepository、IService 的NET Core開源項目
github:https://github.com/anjoy8/Blog.Core
3、EF 自動生成 Entity、EntityConfiguration、DbContext 等
方案一、EntityFramework Reverse POCO Generator (EF實體生成T4模板) -- 暫時不支持efcore
下載:https://marketplace.visualstudio.com/items?itemName=SimonHughes.EntityFrameworkReversePOCOGenerator
Todo
Support EF Core
Support other databases: PostgreSQL, Oracle, MySQL, etc.
github:https://github.com/sjh37/EntityFramework-Reverse-POCO-Code-First-Generator
使用:1、安裝 2、引用 EntityFramework 類庫 3、右鍵項目->添加新項 -> 選擇reverse poco (必須先完成第二步,才能看到此選項)
方案二、Entity Framework Power Tools -- 暫時不支持efcore
EF Power Tools社區版本(支持最新vs,暫不支持efcore)
推薦使用方案一(EntityFramework Reverse POCO Generator)
方案三、EF Core 官方CLI,支持數據庫遷移,生成 Entity、EntityConfiguration 等代碼
nuget包:Microsoft.EntityFrameworkCore.Design
Entity Framework Core 命令行工具介紹【官方】
相關文章推薦:
《T4常用技巧》 ------ 簡潔的介紹了T4中幾個常用技巧的使用方式