在這之前,我寫了一系列關于代碼生成和T4相關的文章,而我現在也試圖将T4引入我們自己的開發架構。在實踐中遇到了一些問題,也解決了不少問題。如果你也在進行T4相關的開發,相信你也一定會遇到這些問題。為此,特意将這些問題和解決方案與朋友們分享,希望在遇到這些問題的時候少走彎路。本篇文章介紹的是兩個重要的話題:程式集鎖定和調試。
目錄 一、程式集引用導緻的編譯問題 二、T4引擎對引用程式集的鎖定 三、Debugger.Break導緻VS 2010的Crash 四、在Debugger.Break之前加上Debugger.Launch
為了讓讀者對“程式集鎖定”,以及由它造成的開發上的不便有一個深刻的認識,我特意寫了一個小例子。如右圖所示的解決方案包含兩個項目:Lib和T4。其中我們的T4項目中定義了一個叫作HelloWorld.tt的模闆檔案,該檔案需要使用到定義在Lib項目中的某個類型。是以,HelloWorld.tt模闆檔案中需要通過<#@Assembly…#>指令引用Lib項目編譯生成的程式集(Artech.T4Template.Lib.dll)。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuITZ4QGZ4QjZmRjZygDZiJmYyQTZyEDZ0YDN3UGZ2EWZfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
using System;
public class HelloWord
{
static void Main()
<# foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
{#>
Console.WriteLine("Hello, {0}!", "<#=person#>");
<# } #>
}
當你儲存該T4模闆,T4引擎将觸發并進行代碼生成工作,但是此時如果你試圖編譯被引用(實際上是生成的程式集被引用)的Lib項目,将會出現如下所示的編譯錯誤。錯誤資訊為:“Unable to copy file "obj\Debug\Artech.T4Template.Lib.dll" to "bin\Debug\Artech.T4Template.Lib.dll". The process cannot access the file 'bin\Debug\Artech.T4Template.Lib.dll' because it is being used by another process.”,即之前生成的程式集正在被使用,是以不能将生成的程式集拷貝到編譯目标目錄下。
實際上這個程式集的使用者正是T4引擎。出于提高性能考慮,T4引擎在進行基于代碼生成的模闆轉換(Template Transformation)的時候,會始終重用同一個AppDomain。由于該AppDomain不會自動解除安裝,這就會導緻該AppDomain始終鎖定所有被它加載的程式集。如果我們需要釋放程式集,我們不得不重新開機VS。但是,對于T4模闆的開發調試階段,這種通過重新啟動VS的方式去釋放程式集以確定我們的項目能夠成功編譯是不能接受的。
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="$(SolutionDir)Lib\Bin\Debug\Artech.T4Template.Lib.dll" #>
<#@ VolatileAssembly…#>的實作原理其實挺簡單的,就是在加載的時候并不是直接加載指定的源程式集,而是建立一個新的程式集拷貝。
VS和一些T4編輯器雖然給了基本的智能感覺支援,但是在絕大部分我們相當于在編寫純文字的腳本,是以對于一些比較複雜的模闆轉換邏輯,我們需要通過Debug的方式去發現一些無法避免的問題。關于T4模闆的Debug,你Google一下會搜出一大堆。在這些“大衆化”的Debug解決方案中都包含兩點:
在<#@ template…#>指令中将debug屬性設定為true;
按照這兩點,我們改寫了我們的T4模闆,在foreach語句之前加上<# Debugger.Break(); #>,并通過<#@import…#>指令導入System.Diagnostics命名空間。我不知道在VS 2008下這種解決方案是否可行,但是如果你使用的是VS 2010,這肯定會導緻整個VS的崩潰。當你儲存TT檔案的時候,如右圖所示的對話框彈出來,随之伴随整個VS的Crash。
<#@ import namespace="System.Diagnostics" #>
<# Debugger.Break(); #>
為了避免Debugger.Break導緻的VS崩潰,隻需要在之前多加一句代碼即可,既Debugger.Launch。為此我們對我們的T4模闆略加修改:
<#
Debugger.Launch();
Debugger.Break();
foreach( var person in Artech.T4Template.HelloWorldHelper.GetPersons())
<#} #>
現在如果你儲存該TT檔案,VS會彈出如下一個對話框讓你選在是否進行Debug。如果需要進行Debug,選擇“Yes, debug devenv.exe”。