CODE-利用T4依XML產生多個資料物件
2 |
遇到的需求是有現成的XML資料定義檔如下
<?xml version="1.0" encoding="utf-8" ?>
<datatable>
<table id="Running" version="1.0">
<col name="RunDateTime" datatype="System.DateTime" pkey="Y"/>
<col name="RunnerId" datatype="System.String" pkey="Y"/>
<col name="Record" datatype="System.String"/>
</table>
<table id="Runner" version="1.0">
<col name="RunnerId" datatype="System.String" pkey="Y"/>
<col name="Name" datatype="System.String" />
<col name="Birthday" datatype="System.DateTime" />
</table>
</datatable>
希望為其中每個Table各產生一個C#類別轡下:
using System.ComponentModel.DataAnnotations;
public class Runner
{
[Key]
public System.String RunnerId { get; set; }
public System.String Name { get; set; }
public System.DateTime Birthday { get; set; }
}
在過去,我會自己寫Code,解析XML文件再用StringBuilder組裝C#類別的內容。但既然已經學會T4範本程式產生器這等好物,自然沒有再走回頭路的道理。
由XML產生C#類別的邏輯很簡單,唯一的小挑戰是一般T4範例多是一個.tt檔產生一個.cs檔,而這個需求得依照XML定義一次產生多個.cs檔案。爬文找到範例,單一.tt要輸出多個檔案的原理,是在執行過程由GenerationEnvironment.ToString()取得當時累積的輸出結果,以File.WriteAllText()寫入檔案後,執行GenerationEnvironment.Clear()清掉已寫到檔案的內容,再執行下個類別的程式碼產生邏輯。
T4程式碼如下:
<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".log" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ Assembly Name="System.Xml.dll" #>
<#@ Assembly Name="System.Xml.Linq.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#
string currPath = Path.GetDirectoryName(Host.TemplateFile);
XDocument xd = XDocument.Load(Path.Combine(currPath, "marathon.xml"));
foreach (var t in xd.Root.Elements()) {
string tableName = t.Attribute("id").Value;
#>
using System.ComponentModel.DataAnnotations;
public class <#= tableName #>
{
<#
foreach (var c in t.Elements()) {
string name = c.Attribute("name").Value;
string type = c.Attribute("datatype").Value;
bool isPK = c.Attribute("pkey") != null;
if (isPK) {
#>
[Key]
<#
}
#>
public <#= type #> <#= name #> { get; set; }
<#
}
#>
}
<#
string relativeOutputFilePath = "Code\\" + tableName + ".cs";
string outputFilePath = Path.Combine(currPath, relativeOutputFilePath);
TemplateHelper.WriteTemplateOutputToFile(outputFilePath, GenerationEnvironment);
}
#>
<#+
//REF: http://bit.ly/X9wTu0
public class TemplateHelper
{
public static void WriteTemplateOutputToFile(
string outputFilePath,
System.Text.StringBuilder genEnvironment)
{
System.IO.File.WriteAllText(outputFilePath, genEnvironment.ToString());
genEnvironment.Clear();
}
}
#>
補充幾點:
- 預設T4並未參照System.Xml.dll及System.Xml.Linq.dll,記得要在最前方透過<#@ Assembly #>及<#@ import #>加入參照。
- 透過Host.TemplateFile可取得T4檔案所在實體路徑,可用來決定輸出檔案路徑。
- T4檔末端透過<#+ #>宣告了共用函數TemplateHelper.WriteTemplateOutputToFile(),透過其將產生結果寫入檔案。
Comments
# by nathan
請問這個能根據某SP回來的TABLE,建立其schema的cs檔嗎?
# by Jeffrey
to nathan, 我想到的做法是用ADO.NET執行SP取得DataTable,由DataColumn集合可取得欄位名稱及資料型別資訊,即能掌握Schema自動產生類別。